export function getIngestionTitle(ingestionType: IngestionComboTypeWithExtra): string {
  switch (ingestionType) {
    case 'index_parallel:local':
      return 'Local disk';

    case 'index_parallel:http':
      return 'HTTP(s)';

    case 'index_parallel:druid':
      return 'Reindex from Druid';

    case 'index_parallel:inline':
      return 'Paste data';

    case 'index_parallel:s3':
      return 'Amazon S3';

    case 'index_parallel:google':
      return 'Google Cloud Storage';

    case 'index_parallel:hdfs':
      return 'HDFS';

    case 'kafka':
      return 'Apache Kafka';

    case 'kinesis':
      return 'Amazon Kinesis';

    case 'hadoop':
      return 'HDFS';

    case 'example':
      return 'Example data';

    case 'other':
      return 'Other';

    default:
      return 'Unknown ingestion';
  }
}


export function getIngestionImage(ingestionType: IngestionComboTypeWithExtra): string {
  const parts = ingestionType.split(':');
  if (parts.length === 2) return parts[1].toLowerCase();
  return ingestionType;
}


export function getIngestionDocLink(spec: IngestionSpec): string {
  const type = getSpecType(spec);

  switch (type) {
    case 'kafka':
      return `https://druid.apache.org/docs/${DRUID_DOCS_VERSION}/development/extensions-core/kafka-ingestion.html`;

    case 'kinesis':
      return `https://druid.apache.org/docs/${DRUID_DOCS_VERSION}/development/extensions-core/kinesis-ingestion.html`;

    default:
      return `https://druid.apache.org/docs/${DRUID_DOCS_VERSION}/ingestion/native-batch.html#firehoses`;
  }
}


export function getRequiredModule(ingestionType: IngestionComboTypeWithExtra): string | undefined {
  switch (ingestionType) {
    case 'index_parallel:s3':
      return 'druid-s3-extensions';

    case 'index_parallel:google':
      return 'druid-google-extensions';

    case 'index_parallel:hdfs':
      return 'druid-hdfs-storage';

    case 'kafka':
      return 'druid-kafka-indexing-service';

    case 'kinesis':
      return 'druid-kinesis-indexing-service';

    default:
      return;
  }
}


export function getDimensionMode(spec: IngestionSpec): DimensionMode {
  const dimensions = deepGet(spec, 'dataSchema.dimensionsSpec.dimensions') || EMPTY_ARRAY;
  return Array.isArray(dimensions) && dimensions.length === 0 ? 'auto-detect' : 'specific';
}


export function getRollup(spec: IngestionSpec): boolean {
  const specRollup = deepGet(spec, 'dataSchema.granularitySpec.rollup');
  return typeof specRollup === 'boolean' ? specRollup : true;
}


export function getSpecType(spec: Partial<IngestionSpec>): IngestionType {
  return (
    deepGet(spec, 'type') ||
    deepGet(spec, 'ioConfig.type') ||
    deepGet(spec, 'tuningConfig.type') ||
    'index_parallel'
  );
}


export function isTask(spec: IngestionSpec) {
  const type = String(getSpecType(spec));
  return (
    type.startsWith('index_') ||
    ['index', 'compact', 'kill', 'append', 'merge', 'same_interval_merge'].includes(type)
  );
}


export function isDruidSource(spec: IngestionSpec): boolean {
  return deepGet(spec, 'ioConfig.inputSource.type') === 'druid';
}


/**
 * Make sure that the types are set in the root, ioConfig, and tuningConfig
 * @param spec
 */
export function normalizeSpec(spec: Partial<IngestionSpec>): IngestionSpec {
  if (!spec || typeof spec !== 'object') {
    // This does not match the type of IngestionSpec but this dialog is robust enough to deal with anything but spec must be an object
    spec = {};
  }

  // Make sure that if we actually get a task payload we extract the spec
  if (typeof (spec as any).spec === 'object') spec = (spec as any).spec;

  const specType = getSpecType(spec);
  if (!specType) return spec as IngestionSpec;
  if (!deepGet(spec, 'type')) spec = deepSet(spec, 'type', specType);
  if (!deepGet(spec, 'ioConfig.type')) spec = deepSet(spec, 'ioConfig.type', specType);
  if (!deepGet(spec, 'tuningConfig.type')) spec = deepSet(spec, 'tuningConfig.type', specType);
  return spec as IngestionSpec;
}


export function getInputFormatFormFields() {
  return INPUT_FORMAT_FORM_FIELDS;
}


export function issueWithInputFormat(inputFormat: InputFormat | undefined): string | undefined {
  if (!inputFormat) return 'no input format';
  if (!inputFormat.type) return 'missing a type';
  switch (inputFormat.type) {
    case 'regex':
      if (!inputFormat.pattern) return "must have a 'pattern'";
      break;

    case 'javascript':
      if (!inputFormat['function']) return "must have a 'function'";
      break;
  }
  return;
}


export function inputFormatCanFlatten(inputFormat: InputFormat): boolean {
  const inputFormatType = inputFormat.type;
  return inputFormatType === 'json' || inputFormatType === 'parquet' || inputFormatType === 'orc';
}


export function getTimestampSpecColumn(timestampSpec: TimestampSpec) {
  // https://github.com/apache/incubator-druid/blob/master/core/src/main/java/org/apache/druid/data/input/impl/TimestampSpec.java#L44
  return timestampSpec.column || 'timestamp';
}


export function getDummyTimestampSpec() {
  return DUMMY_TIMESTAMP_SPEC;
}


export function getConstantTimestampSpec() {
  return CONSTANT_TIMESTAMP_SPEC;
}


export function isColumnTimestampSpec(timestampSpec: TimestampSpec) {
  return (deepGet(timestampSpec, 'column') || 'timestamp') !== NO_SUCH_COLUMN;
}


export function getTimestampSpecFormFields(timestampSpec: TimestampSpec) {
  if (isColumnTimestampSpec(timestampSpec)) {
    return TIMESTAMP_SPEC_FORM_FIELDS;
  } else {
    return CONSTANT_TIMESTAMP_SPEC_FORM_FIELDS;
  }
}


export function issueWithTimestampSpec(
  timestampSpec: TimestampSpec | undefined,
): string | undefined {
  if (!timestampSpec) return 'no spec';
  if (!timestampSpec.column && !timestampSpec.missingValue) return 'timestamp spec is blank';
  return;
}


export function getDimensionSpecFormFields() {
  return DIMENSION_SPEC_FORM_FIELDS;
}


export function getDimensionSpecName(dimensionSpec: string | DimensionSpec): string {
  return typeof dimensionSpec === 'string' ? dimensionSpec : dimensionSpec.name;
}


export function getDimensionSpecType(dimensionSpec: string | DimensionSpec): string {
  return typeof dimensionSpec === 'string' ? 'string' : dimensionSpec.type;
}


export function inflateDimensionSpec(dimensionSpec: string | DimensionSpec): DimensionSpec {
  return typeof dimensionSpec === 'string'
    ? { name: dimensionSpec, type: 'string' }
    : dimensionSpec;
}


export function getFlattenFieldFormFields() {
  return FLATTEN_FIELD_FORM_FIELDS;
}


export function getTransformFormFields() {
  return TRANSFORM_FORM_FIELDS;
}


export function getMetricSpecFormFields() {
  return METRIC_SPEC_FORM_FIELDS;
}


export function getMetricSpecName(metricSpec: MetricSpec): string {
  return (
    metricSpec.name || (metricSpec.aggregator ? getMetricSpecName(metricSpec.aggregator) : '?')
  );
}


export function invalidIoConfig(ioConfig: IoConfig): boolean {
  return (
    (ioConfig.type === 'kafka' && ioConfig.useEarliestOffset == null) ||
    (ioConfig.type === 'kinesis' && ioConfig.useEarliestSequenceNumber == null)
  );
}


export function getIoConfigFormFields(ingestionComboType: IngestionComboType): Field<IoConfig>[] {
  const inputSourceType: Field<IoConfig> = {
    name: 'inputSource.type',
    label: 'Source type',
    type: 'string',
    suggestions: ['local', 'http', 'inline', 's3', 'google', 'hdfs'],
    info: (
      <p>
        Druid connects to raw data through{' '}
        <ExternalLink
          href={`https://druid.apache.org/docs/${DRUID_DOCS_VERSION}/ingestion/firehose.html`}
        >
          inputSources
        </ExternalLink>
        . You can change your selected inputSource here.
      </p>
    ),
  }


function nonEmptyArray(a: any) {
  return Array.isArray(a) && Boolean(a.length);
}


function issueWithInputSource(inputSource: InputSource | undefined): string | undefined {
  if (!inputSource) return 'does not exist';
  if (!inputSource.type) return 'missing a type';
  switch (inputSource.type) {
    case 'local':
      if (!inputSource.baseDir) return `must have a 'baseDir'`;
      if (!inputSource.filter) return `must have a 'filter'`;
      break;

    case 'http':
      if (!nonEmptyArray(inputSource.uris)) {
        return 'must have at least one uri';
      }
      break;

    case 'druid':
      if (!inputSource.dataSource) return `must have a 'dataSource'`;
      if (!inputSource.interval) return `must have an 'interval'`;
      break;

    case 'inline':
      if (!inputSource.data) return `must have 'data'`;
      break;

    case 's3':
      if (!nonEmptyArray(inputSource.uris) && !nonEmptyArray(inputSource.prefixes)) {
        return 'must have at least one uri or prefix';
      }
      break;

    case 'google':
      if (!nonEmptyArray(inputSource.blobs)) {
        return 'must have at least one blob';
      }
      break;

    case 'hdfs':
      if (!inputSource.paths) {
        return 'must have paths';
      }
      break;
  }
  return;
}


export function issueWithIoConfig(
  ioConfig: IoConfig | undefined,
  ignoreInputFormat = false,
): string | undefined {
  if (!ioConfig) return 'does not exist';
  if (!ioConfig.type) return 'missing a type';
  switch (ioConfig.type) {
    case 'index':
    case 'index_parallel':
      if (issueWithInputSource(ioConfig.inputSource)) {
        return `inputSource: '${issueWithInputSource(ioConfig.inputSource)}'`;
      }
      break;

    case 'kafka':
      if (!ioConfig.topic) return 'must have a topic';
      break;

    case 'kinesis':
      if (!ioConfig.stream) return 'must have a stream';
      break;
  }

  if (!ignoreInputFormat && issueWithInputFormat(ioConfig.inputFormat)) {
    return `inputFormat: '${issueWithInputFormat(ioConfig.inputFormat)}'`;
  }

  return;
}


export function getIoConfigTuningFormFields(
  ingestionComboType: IngestionComboType,
): Field<IoConfig>[] {
  switch (ingestionComboType) {
    case 'index_parallel:http':
    case 'index_parallel:s3':
    case 'index_parallel:google':
    case 'index_parallel:hdfs':
      return [
        {
          name: 'inputSource.fetchTimeout',
          label: 'Fetch timeout',
          type: 'number',
          defaultValue: 60000,
          info: (
            <>

Timeout for fetching the object.

            </>
          ),
        },
        {
          name: 'inputSource.maxFetchRetry',
          label: 'Max fetch retry',
          type: 'number',
          defaultValue: 3,
          info: (
            <>

Maximum retry for fetching the object.

            </>
          ),
        },
        {
          name: 'inputSource.maxCacheCapacityBytes',
          label: 'Max cache capacity bytes',
          type: 'number',
          defaultValue: 1073741824,
          info: (
            <>

                Maximum size of the cache space in bytes. 0 means disabling cache. Cached files are
                not removed until the ingestion task completes.

            </>
          ),
        },
        {
          name: 'inputSource.maxFetchCapacityBytes',
          label: 'Max fetch capacity bytes',
          type: 'number',
          defaultValue: 1073741824,
          info: (
            <>

                Maximum size of the fetch space in bytes. 0 means disabling prefetch. Prefetched
                files are removed immediately once they are read.

            </>
          ),
        },
        {
          name: 'inputSource.prefetchTriggerBytes',
          label: 'Prefetch trigger bytes',
          type: 'number',
          placeholder: 'maxFetchCapacityBytes / 2',
          info: (
            <>

Threshold to trigger prefetching the objects.

            </>
          ),
        },
      ];

    case 'index_parallel:local':
    case 'index_parallel:inline':
      return [];

    case 'index_parallel:druid':
      return [
        {
          name: 'inputSource.maxFetchCapacityBytes',
          label: 'Max fetch capacity bytes',
          type: 'number',
          defaultValue: 157286400,
          info: (

              When used with the native parallel index task, the maximum number of bytes of input
              segments to process in a single task. If a single segment is larger than this number,
              it will be processed by itself in a single task (input segments are never split across
              tasks). Defaults to 150MB.

          ),
        }


// ---------------------------------------
function filterIsFilename(filter: string): boolean {
  return !/[*?]/.test(filter);
}


function filenameFromPath(path: string): string | undefined {
  const m = path.match(/([^\/.]+)[^\/]*?\/?$/);
  if (!m) return;
  return m[1];
}


function basenameFromFilename(filename: string): string | undefined {
  return filename.split('.')[0];
}


export function fillDataSourceNameIfNeeded(spec: IngestionSpec): IngestionSpec {
  const possibleName = guessDataSourceName(spec);
  if (!possibleName) return spec;
  return deepSet(spec, 'dataSchema.dataSource', possibleName);
}


export function guessDataSourceName(spec: IngestionSpec): string | undefined {
  const ioConfig = deepGet(spec, 'ioConfig');
  if (!ioConfig) return;

  switch (ioConfig.type) {
    case 'index':
    case 'index_parallel':
      const inputSource = ioConfig.inputSource;
      if (!inputSource) return;

      switch (inputSource.type) {
        case 'local':
          if (inputSource.filter && filterIsFilename(inputSource.filter)) {
            return basenameFromFilename(inputSource.filter);
          } else if (inputSource.baseDir) {
            return filenameFromPath(inputSource.baseDir);
          } else {
            return;
          }

        case 's3':
          const s3Path =
            (inputSource.uris || EMPTY_ARRAY)[0] || (inputSource.prefixes || EMPTY_ARRAY)[0];
          return s3Path ? filenameFromPath(s3Path) : undefined;

        case 'http':
          return Array.isArray(inputSource.uris)
            ? filenameFromPath(inputSource.uris[0])
            : undefined;

        case 'druid':
          return inputSource.dataSource;

        case 'inline':
          return 'inline_data';
      }

      return;

    case 'kafka':
      return ioConfig.topic;

    case 'kinesis':
      return ioConfig.stream;

    default:
      return;
  }
}


export function adjustTuningConfig(tuningConfig: TuningConfig) {
  const tuningConfigType = deepGet(tuningConfig, 'type');
  if (tuningConfigType !== 'index_parallel') return tuningConfig;

  const partitionsSpecType = deepGet(tuningConfig, 'partitionsSpec.type');
  if (tuningConfig.forceGuaranteedRollup) {
    if (partitionsSpecType !== 'hashed' && partitionsSpecType !== 'single_dim') {
      tuningConfig = deepSet(tuningConfig, 'partitionsSpec', { type: 'hashed' });
    }
  } else {
    if (partitionsSpecType !== 'dynamic') {
      tuningConfig = deepSet(tuningConfig, 'partitionsSpec', { type: 'dynamic' });
    }
  }
  return tuningConfig;
}


export function invalidTuningConfig(tuningConfig: TuningConfig, intervals: any): boolean {
  if (tuningConfig.type !== 'index_parallel' || !tuningConfig.forceGuaranteedRollup) return false;

  if (!intervals) return true;
  switch (deepGet(tuningConfig, 'partitionsSpec.type')) {
    case 'hashed':
      if (!deepGet(tuningConfig, 'partitionsSpec.numShards')) return true;
      break;

    case 'single_dim':
      if (!deepGet(tuningConfig, 'partitionsSpec.partitionDimension')) return true;
      if (
        !deepGet(tuningConfig, 'partitionsSpec.targetRowsPerSegment') &&
        !deepGet(tuningConfig, 'partitionsSpec.maxRowsPerSegment')
      ) {
        return true;
      }
  }

  return false;
}


export function getPartitionRelatedTuningSpecFormFields(
  specType: IngestionType,
): Field<TuningConfig>[] {
  switch (specType) {
    case 'index_parallel':
      return [
        {
          name: 'forceGuaranteedRollup',
          type: 'boolean',
          defaultValue: false,
          info: (

              Forces guaranteeing the perfect rollup. The perfect rollup optimizes the total size of
              generated segments and querying time while indexing time will be increased. If this is
              set to true, the index task will read the entire input data twice: one for finding the
              optimal number of partitions per time chunk and one for generating segments.

          ),
          adjustment: adjustTuningConfig,
        },
        {
          name: 'partitionsSpec.type',
          label: 'Partitioning type',
          type: 'string',
          suggestions: (t: TuningConfig) =>
            t.forceGuaranteedRollup ? ['hashed', 'single_dim'] : ['dynamic'],
          info: (

              For perfect rollup, you should use either hashed (partitioning based on
              the hash of dimensions in each row) or single_dim (based on ranges of a
              single dimension. For best-effort rollup, you should use dynamic.

          ),
        },
        // partitionsSpec type: dynamic
        {
          name: 'partitionsSpec.maxRowsPerSegment',
          label: 'Max rows per segment',
          type: 'number',
          defaultValue: 5000000,
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'dynamic',
          info: <>Determines how many rows are in each segment.</>,
        },
        {
          name: 'partitionsSpec.maxTotalRows',
          label: 'Max total rows',
          type: 'number',
          defaultValue: 20000000,
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'dynamic',
          info: <>Total number of rows in segments waiting for being pushed.</>,
        },
        // partitionsSpec type: hashed
        {
          name: 'partitionsSpec.numShards',
          label: 'Num shards',
          type: 'number',
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'hashed',
          required: true,
          info: (
            <>
              Directly specify the number of shards to create. If this is specified and 'intervals'
              is specified in the granularitySpec, the index task can skip the determine
              intervals/partitions pass through the data. numShards cannot be specified if
              maxRowsPerSegment is set.
            </>
          ),
        },
        {
          name: 'partitionsSpec.partitionDimensions',
          label: 'Partition dimensions',
          type: 'string-array',
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'hashed',
          info:
The dimensions to partition on. Leave blank to select all dimensions.
,
        },
        // partitionsSpec type: single_dim
        {
          name: 'partitionsSpec.partitionDimension',
          label: 'Partition dimension',
          type: 'string',
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'single_dim',
          required: true,
          info:
The dimension to partition on.
,
        },
        {
          name: 'partitionsSpec.targetRowsPerSegment',
          label: 'Target rows per segment',
          type: 'number',
          zeroMeansUndefined: true,
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'single_dim',
          required: (t: TuningConfig) =>
            !deepGet(t, 'partitionsSpec.targetRowsPerSegment') &&
            !deepGet(t, 'partitionsSpec.maxRowsPerSegment'),
          info: (

              Target number of rows to include in a partition, should be a number that targets
              segments of 500MB~1GB.

          ),
        },
        {
          name: 'partitionsSpec.maxRowsPerSegment',
          label: 'Max rows per segment',
          type: 'number',
          zeroMeansUndefined: true,
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'single_dim',
          required: (t: TuningConfig) =>
            !deepGet(t, 'partitionsSpec.targetRowsPerSegment') &&
            !deepGet(t, 'partitionsSpec.maxRowsPerSegment'),
          info:
Maximum number of rows to include in a partition.
,
        },
        {
          name: 'partitionsSpec.assumeGrouped',
          label: 'Assume grouped',
          type: 'boolean',
          defaultValue: false,
          defined: (t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'single_dim',
          info: (

              Assume that input data has already been grouped on time and dimensions. Ingestion will
              run faster, but may choose sub-optimal partitions if this assumption is violated.

          ),
        },
      ];

    case 'kafka':
    case 'kinesis':
      return [
        {
          name: 'maxRowsPerSegment',
          type: 'number',
          defaultValue: 5000000,
          info: <>Determines how many rows are in each segment.</>,
        },
        {
          name: 'maxTotalRows',
          type: 'number',
          defaultValue: 20000000,
          info: <>Total number of rows in segments waiting for being pushed.</>,
        },
      ];
  }


export function getTuningSpecFormFields() {
  return TUNING_CONFIG_FORM_FIELDS;
}


// --------------
export function updateIngestionType(
  spec: IngestionSpec,
  comboType: IngestionComboType,
): IngestionSpec {
  const [ingestionType, inputSourceType] = comboType.split(':');
  const ioAndTuningConfigType = ingestionTypeToIoAndTuningConfigType(
    ingestionType as IngestionType,
  );

  let newSpec = spec;
  newSpec = deepSet(newSpec, 'type', ingestionType);
  newSpec = deepSet(newSpec, 'ioConfig.type', ioAndTuningConfigType);
  newSpec = deepSet(newSpec, 'tuningConfig.type', ioAndTuningConfigType);

  if (inputSourceType) {
    newSpec = deepSet(newSpec, 'ioConfig.inputSource', { type: inputSourceType });

    if (inputSourceType === 'local') {
      newSpec = deepSet(newSpec, 'ioConfig.inputSource.filter', '*');
    }
  }

  if (!deepGet(spec, 'dataSchema.dataSource')) {
    newSpec = deepSet(newSpec, 'dataSchema.dataSource', 'new-data-source');
  }

  if (!deepGet(spec, 'dataSchema.granularitySpec')) {
    const granularitySpec: GranularitySpec = {
      type: 'uniform',
      queryGranularity: 'HOUR',
    };
    if (ingestionType !== 'index_parallel') {
      granularitySpec.segmentGranularity = 'HOUR';
    }

    newSpec = deepSet(newSpec, 'dataSchema.granularitySpec', granularitySpec);
  }

  if (!deepGet(spec, 'dataSchema.timestampSpec')) {
    newSpec = deepSet(newSpec, 'dataSchema.timestampSpec', getDummyTimestampSpec());
  }

  if (!deepGet(spec, 'dataSchema.dimensionsSpec')) {
    newSpec = deepSet(newSpec, 'dataSchema.dimensionsSpec', {});
  }

  return newSpec;
}


export function fillInputFormat(spec: IngestionSpec, sampleData: string[]): IngestionSpec {
  const inputFormat = guessInputFormat(sampleData);
  if (!inputFormat) return spec;

  return deepSet(spec, 'ioConfig.inputFormat', inputFormat);
}


function guessInputFormat(sampleData: string[]): InputFormat | undefined {
  const sampleDatum = sampleData[0];
  if (!sampleDatum) return;

  if (sampleDatum.startsWith('{') && sampleDatum.endsWith('}')) {
    return inputFormatFromType('json');
  }

  if (sampleDatum.split('\t').length > 3) {
    return inputFormatFromType('tsv', !/\t\d+\t/.test(sampleDatum));
  }

  if (sampleDatum.split(',').length > 3) {
    return inputFormatFromType('csv', !/,\d+,/.test(sampleDatum));
  }

  if (sampleDatum.startsWith('PAR1')) {
    return inputFormatFromType('parquet');
  }

  if (sampleDatum.startsWith('ORC')) {
    return inputFormatFromType('orc');
  }

  return inputFormatFromType('regex');
}


function inputFormatFromType(type: string, findColumnsFromHeader?: boolean): InputFormat {
  const inputFormat: InputFormat = { type };

  if (type === 'regex') {
    inputFormat.pattern = '(.*)';
    inputFormat.columns = ['column1'];
  }

  if (typeof findColumnsFromHeader === 'boolean') {
    inputFormat.findColumnsFromHeader = findColumnsFromHeader;
  }

  return inputFormat;
}


export function splitFilter(filter: DruidFilter | null): DimensionFiltersWithRest {
  const inputAndFilters: DruidFilter[] = filter
    ? filter.type === 'and' && Array.isArray(filter.fields)
      ? filter.fields
      : [filter]
    : EMPTY_ARRAY;
  const dimensionFilters: DruidFilter[] = inputAndFilters.filter(
    f => typeof f.dimension === 'string',
  );
  const restFilters: DruidFilter[] = inputAndFilters.filter(f => typeof f.dimension !== 'string');

  return {
    dimensionFilters,
    restFilter: restFilters.length
      ? restFilters.length > 1
        ? { type: 'and', filters: restFilters }
        : restFilters[0]
      : undefined,
  };
}


export function joinFilter(
  dimensionFiltersWithRest: DimensionFiltersWithRest,
): DruidFilter | undefined {
  const { dimensionFilters, restFilter } = dimensionFiltersWithRest;
  let newFields = dimensionFilters || EMPTY_ARRAY;
  if (restFilter && restFilter.type) newFields = newFields.concat([restFilter]);

  if (!newFields.length) return;
  if (newFields.length === 1) return newFields[0];
  return { type: 'and', fields: newFields };
}


export function getFilterFormFields() {
  return FILTER_FORM_FIELDS;
}


export function upgradeSpec(spec: any): any {
  if (deepGet(spec, 'ioConfig.firehose')) {
    switch (deepGet(spec, 'ioConfig.firehose.type')) {
      case 'static-s3':
        deepSet(spec, 'ioConfig.firehose.type', 's3');
        break;

      case 'static-google-blobstore':
        deepSet(spec, 'ioConfig.firehose.type', 'google');
        break;
    }

    spec = deepMove(spec, 'ioConfig.firehose', 'ioConfig.inputSource');
    spec = deepMove(spec, 'dataSchema.parser.parseSpec.timestampSpec', 'dataSchema.timestampSpec');
    spec = deepMove(
      spec,
      'dataSchema.parser.parseSpec.dimensionsSpec',
      'dataSchema.dimensionsSpec',
    );
    spec = deepMove(spec, 'dataSchema.parser.parseSpec', 'ioConfig.inputFormat');
    spec = deepDelete(spec, 'dataSchema.parser');
    spec = deepMove(spec, 'ioConfig.inputFormat.format', 'ioConfig.inputFormat.type');
  }
  return spec;
}


export function downgradeSpec(spec: any): any {
  if (deepGet(spec, 'ioConfig.inputSource')) {
    spec = deepMove(spec, 'ioConfig.inputFormat.type', 'ioConfig.inputFormat.format');
    spec = deepSet(spec, 'dataSchema.parser', { type: 'string' });
    spec = deepMove(spec, 'ioConfig.inputFormat', 'dataSchema.parser.parseSpec');
    spec = deepMove(
      spec,
      'dataSchema.dimensionsSpec',
      'dataSchema.parser.parseSpec.dimensionsSpec',
    );
    spec = deepMove(spec, 'dataSchema.timestampSpec', 'dataSchema.parser.parseSpec.timestampSpec');
    spec = deepMove(spec, 'ioConfig.inputSource', 'ioConfig.firehose');

    switch (deepGet(spec, 'ioConfig.firehose.type')) {
      case 's3':
        deepSet(spec, 'ioConfig.firehose.type', 'static-s3');
        break;

      case 'google':
        deepSet(spec, 'ioConfig.firehose.type', 'static-google-blobstore');
        break;
    }
  }
  return spec;
}


(p: InputFormat) => p.type === 'regex'


(p: InputFormat) => p.type === 'javascript'


(p: InputFormat) => p.type === 'csv' || p.type === 'tsv'


(p: InputFormat) =>
      ((p.type === 'csv' || p.type === 'tsv') && !p.findColumnsFromHeader) || p.type === 'regex'


(p: InputFormat) => p.type === 'csv' || p.type === 'tsv' || p.type === 'regex'


(p: InputFormat) => p.type === 'parquet' || p.type === 'orc'


(timestampSpec: TimestampSpec) => isColumnTimestampSpec(timestampSpec)


(dimensionSpec: DimensionSpec) => dimensionSpec.type === 'string'


(flattenField: FlattenField) =>
      flattenField.type === 'path' || flattenField.type === 'jq'


m => m.type !== 'filtered'


m => {
      return ['stringFirst', 'stringLast'].includes(m.type);
    }


m => m.type === 'filtered'


m => m.type === 'thetaSketch'


m => m.type === 'HLLSketchBuild' || m.type === 'HLLSketchMerge'


m => m.type === 'quantilesDoublesSketch'


m => m.type === 'momentSketch'


m => m.type === 'fixedBucketsHistogram'


m => m.type === 'hyperUnique'


ioConfig => !deepGet(ioConfig, 'inputSource.prefixes')


ioConfig => !deepGet(ioConfig, 'inputSource.uris')


(i: IoConfig) => i.type === 'kafka'


(i: IoConfig) => i.type === 'kinesis'


(t: TuningConfig) =>
            t.forceGuaranteedRollup ? ['hashed', 'single_dim'] : ['dynamic']


(t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'dynamic'


(t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'hashed'


(t: TuningConfig) => deepGet(t, 'partitionsSpec.type') === 'single_dim'


(t: TuningConfig) =>
            !deepGet(t, 'partitionsSpec.targetRowsPerSegment') &&
            !deepGet(t, 'partitionsSpec.maxRowsPerSegment')


(t: TuningConfig) => t.type === 'index_parallel'


(t: TuningConfig) => Boolean(t.type === 'index_parallel' && t.forceGuaranteedRollup)


(t: TuningConfig) => t.type === 'kafka' || t.type === 'kinesis'


(t: TuningConfig) => t.type === 'kafka'


(t: TuningConfig) => t.type === 'kinesis'


f => typeof f.dimension === 'string'


f => typeof f.dimension !== 'string'


(df: DruidFilter) => ['selector', 'in', 'regex', 'like'].includes(df.type)


(df: DruidFilter) => df.type === 'selector'


(df: DruidFilter) => df.type === 'in'


(df: DruidFilter) => ['regex', 'like'].includes(df.type)


(df: DruidFilter) => df.type === 'not'


(df: DruidFilter) => df.type === 'not' && deepGet(df, 'field.type') === 'selector'


(df: DruidFilter) => df.type === 'not' && deepGet(df, 'field.type') === 'in'


(df: DruidFilter) =>
      df.type === 'not' && ['regex', 'like'].includes(deepGet(df, 'field.type'))


export interface IngestionSpec {
  type?: IngestionType;
  ioConfig: IoConfig;
  dataSchema: DataSchema;
  tuningConfig?: TuningConfig;
}


// --------------
export interface DataSchema {
  dataSource: string;
  timestampSpec: TimestampSpec;
  transformSpec?: TransformSpec;
  granularitySpec?: GranularitySpec;
  dimensionsSpec: DimensionsSpec;
  metricsSpec?: MetricSpec[];
}


export interface InputFormat {
  type: string;
  findColumnsFromHeader?: boolean;
  skipHeaderRows?: number;
  columns?: string[];
  listDelimiter?: string;
  pattern?: string;
  function?: string;
  flattenSpec?: FlattenSpec;
}


export interface TimestampSpec {
  column?: string;
  format?: string;
  missingValue?: string;
}


export interface DimensionsSpec {
  dimensions?: (string | DimensionSpec)[];
  dimensionExclusions?: string[];
  spatialDimensions?: any[];
}


export interface DimensionSpec {
  type: string;
  name: string;
  createBitmapIndex?: boolean;
}


export interface FlattenSpec {
  useFieldDiscovery?: boolean;
  fields?: FlattenField[];
}


export interface FlattenField {
  name: string;
  type: string;
  expr: string;
}


export interface TransformSpec {
  transforms?: Transform[];
  filter?: any;
}


export interface Transform {
  type: string;
  name: string;
  expression: string;
}


export interface GranularitySpec {
  type?: string;
  queryGranularity?: string;
  segmentGranularity?: string;
  rollup?: boolean;
  intervals?: string | string[];
}


export interface MetricSpec {
  type: string;
  name?: string;
  fieldName?: string;
  maxStringBytes?: number;
  filterNullValues?: boolean;
  fieldNames?: string[];
  fnAggregate?: string;
  fnCombine?: string;
  fnReset?: string;
  fields?: string[];
  byRow?: boolean;
  round?: boolean;
  isInputHyperUnique?: boolean;
  filter?: any;
  aggregator?: MetricSpec;
}


// --------------
export interface IoConfig {
  type: string;
  inputSource?: InputSource;
  inputFormat?: InputFormat;
  appendToExisting?: boolean;
  topic?: string;
  consumerProperties?: any;
  replicas?: number;
  taskCount?: number;
  taskDuration?: string;
  startDelay?: string;
  period?: string;
  useEarliestOffset?: boolean;
  stream?: string;
  endpoint?: string;
  useEarliestSequenceNumber?: boolean;
}


export interface InputSource {
  type: string;
  baseDir?: string;
  filter?: any;
  uris?: string[];
  prefixes?: string[];
  blobs?: { bucket: string; path: string }[];
  fetchTimeout?: number;

  // druid
  dataSource?: string;
  interval?: string;
  dimensions?: string[];
  metrics?: string[];
  maxInputSegmentBytesPerTask?: number;

  // inline
  data?: string;

  // hdfs
  paths?: string;
}


// --------------
export interface TuningConfig {
  type: string;
  maxRowsInMemory?: number;
  maxBytesInMemory?: number;
  partitionsSpec?: PartitionsSpec;
  maxPendingPersists?: number;
  indexSpec?: IndexSpec;
  forceExtendableShardSpecs?: boolean;
  forceGuaranteedRollup?: boolean;
  reportParseExceptions?: boolean;
  pushTimeout?: number;
  segmentWriteOutMediumFactory?: any;
  intermediateHandoffPeriod?: string;
  handoffConditionTimeout?: number;
  resetOffsetAutomatically?: boolean;
  workerThreads?: number;
  chatThreads?: number;
  chatRetries?: number;
  httpTimeout?: string;
  shutdownTimeout?: string;
  offsetFetchPeriod?: string;
  maxParseExceptions?: number;
  maxSavedParseExceptions?: number;
  recordBufferSize?: number;
  recordBufferOfferTimeout?: number;
  recordBufferFullWait?: number;
  fetchSequenceNumberTimeout?: number;
  fetchThreads?: number;
}


export interface PartitionsSpec {
  type: 'string';

  // For type: dynamic
  maxTotalRows?: number;

  // For type: hashed
  numShards?: number;
  partitionDimensions?: string[];

  // For type: single_dim
  targetRowsPerSegment?: number;
  maxRowsPerSegment?: number;
  partitionDimension?: string;
  assumeGrouped?: boolean;
}


export interface IndexSpec {
  bitmap?: Bitmap;
  dimensionCompression?: string;
  metricCompression?: string;
  longEncoding?: string;
}


export interface Bitmap {
  type: string;
  compressRunOnSerialization?: boolean;
}


export interface DimensionFiltersWithRest {
  dimensionFilters: DruidFilter[];
  restFilter?: DruidFilter;
}


enum type


export type IngestionType = 'kafka' | 'kinesis' | 'index_parallel';


// A combination of IngestionType and inputSourceType
export type IngestionComboType =
  | 'kafka'
  | 'kinesis'
  | 'index_parallel:http'
  | 'index_parallel:local'
  | 'index_parallel:druid'
  | 'index_parallel:inline'
  | 'index_parallel:s3'
  | 'index_parallel:google'
  | 'index_parallel:hdfs';


// Some extra values that can be selected in the initial screen
export type IngestionComboTypeWithExtra = IngestionComboType | 'hadoop' | 'example' | 'other';


export type DimensionMode = 'specific' | 'auto-detect';


type of the


export type DruidFilter = Record<string, any>;


paths (


task (input segments are never split across
              tasks)


be <Code>


* replicas<


tasks (no replication)


events (such as tasks
                succeeding, failing, and reaching their taskDuration)


segments (e


() => {
  const { count } = useModel('product', model => (
    {
      count: model.count
    }
  ))

  return <>

页面三

    <Descriptions title="商品信息">
      <Descriptions.Item label="名称">T-shirt</Descriptions.Item>
      <Descriptions.Item label="价格">799</Descriptions.Item>
      <Descriptions.Item label="数量">{count}</Descriptions.Item>
    </Descriptions>
  </>
}


model => (
    {
      count: model.count
    }
  )


() => {
  let fixture: ComponentFixture<MockComponent>;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        MockComponent,
        CustomFocusDirective,
        CustomFakeFocusDirective,
      ],
      providers: [
        {
          provide: BaseFocusService,
          useClass: MockVisibleFocusService,
        },
      ],
    }).compileComponents();
    fixture = TestBed.createComponent(MockComponent);
  }));

  beforeEach(() => {
    fixture.detectChanges();
  });

  describe('default behaviour', () => {
    let host: DebugElement;
    beforeEach(() => {
      host = fixture.debugElement.query(By.css('#a'));
      fixture.detectChanges();
    });

    it('should not have "mouse-focus" class on the host by default', () => {
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });

    it('should add "mouse-focus" class when mousedown is triggered', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    });

    it('should remove "mouse-focus" class when keydown is triggered after mousedown', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });

    it('should add "mouse-focus" class when mousedown is triggered after keydown', () => {
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    });
  });

  describe('explicitily set disableMouseFocus to false', () => {
    let host: DebugElement;
    beforeEach(() => {
      host = fixture.debugElement.query(By.css('#b'));
      fixture.detectChanges();
    });

    it('should not add "mouse-focus" class when mousedown is triggered', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });
  });

  describe('default behaviour for child directives', () => {
    it('should not add "mouse-focus" class when mousedown is triggered', () => {
      const host = fixture.debugElement.query(By.css('#c'));
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [
        MockComponent,
        CustomFocusDirective,
        CustomFakeFocusDirective,
      ],
      providers: [
        {
          provide: BaseFocusService,
          useClass: MockVisibleFocusService,
        },
      ],
    }).compileComponents();
    fixture = TestBed.createComponent(MockComponent);
  }


() => {
    fixture.detectChanges();
  }


() => {
    let host: DebugElement;
    beforeEach(() => {
      host = fixture.debugElement.query(By.css('#a'));
      fixture.detectChanges();
    });

    it('should not have "mouse-focus" class on the host by default', () => {
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });

    it('should add "mouse-focus" class when mousedown is triggered', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    });

    it('should remove "mouse-focus" class when keydown is triggered after mousedown', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });

    it('should add "mouse-focus" class when mousedown is triggered after keydown', () => {
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    });
  }


() => {
      host = fixture.debugElement.query(By.css('#a'));
      fixture.detectChanges();
    }


() => {
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    }


() => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    }


() => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    }


() => {
      host.triggerEventHandler('keydown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).toContain(
        'mouse-focus'
      );
    }


() => {
    let host: DebugElement;
    beforeEach(() => {
      host = fixture.debugElement.query(By.css('#b'));
      fixture.detectChanges();
    });

    it('should not add "mouse-focus" class when mousedown is triggered', () => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });
  }


() => {
      host = fixture.debugElement.query(By.css('#b'));
      fixture.detectChanges();
    }


() => {
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    }


() => {
    it('should not add "mouse-focus" class when mousedown is triggered', () => {
      const host = fixture.debugElement.query(By.css('#c'));
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    });
  }


() => {
      const host = fixture.debugElement.query(By.css('#c'));
      host.triggerEventHandler('mousedown', MockEvent);
      fixture.detectChanges();
      expect((host.nativeElement as HTMLElement).classList).not.toContain(
        'mouse-focus'
      );
    }


@Directive({
  selector: '[cxVisibleFocus]',
})
class CustomFocusDirective extends VisibleFocusDirective {
  @Input('cxVisibleFocus') protected config: VisibleFocusConfig;

  constructor(
    protected elementRef: ElementRef<HTMLElement>,
    protected service: BaseFocusService
  ) {
    super(elementRef, service);
  }
}


@Directive({
  selector: '[cxCustomFocus]',
})
class CustomFakeFocusDirective extends VisibleFocusDirective {
  protected defaultConfig = {};

  constructor(
    protected elementRef: ElementRef<HTMLElement>,
    protected service: BaseFocusService
  ) {
    super(elementRef, service);
  }
}


@Component({
  selector: 'cx-host',
  template: `

  `,
})
class MockComponent {}


class MockVisibleFocusService {}


() => cell


render () {
    const {
      isSummary,
      column: { render, key, ellipsis },
      row
    } = this
    let cell: VNodeChild
    if (render && !isSummary) {
      cell = render(row, this.index)
    } else {
      if (isSummary) {
        cell = (row[key] as SummaryCell).value
      } else {
        cell = get(row, key) as any
      }
    }
    if (ellipsis && typeof ellipsis === 'object') {
      const { mergedTheme } = this
      return (
        <NEllipsis
          {...ellipsis}
          theme={mergedTheme.peers.Ellipsis}
          themeOverrides={mergedTheme.peerOverrides.Ellipsis}
        >
          {{


({ times }) => times[0]


({ times }) => times[times.length - 1]


({ node, transform, times, values }) => {
      const prevIndex = Math.max(1, times.findIndex((t) => t > elapsed)) - 1
      const nextIndex = prevIndex + 1
      const alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex])
      const size = values.length / times.length
      const prevVal = fromArray(values, prevIndex * size, size)
      const nextVal = fromArray(values, nextIndex * size, size)

      if (transform === "rotation") {
        node.rotation = q.slerp(prevVal as Quaternion, nextVal as Quaternion, alpha)
      } else if (transform === "position") {
        node.position = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      } else if (transform === "scale") {
        node.scale = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      }
      node.localMatrix = m4.compose(node.rotation, node.position, node.scale)
    }


(t) => t > elapsed


(array: ArrayLike<number>, startIndex: number, count: number): Vector3 | Quaternion =>
  Array.from({ length: count }, (_: number, i: number) => array[startIndex + i]) as Vector3 | Quaternion


(_: number, i: number) => array[startIndex + i]


export class Animation {
  public readonly name: string

  private readonly data: AnimationData[]
  private readonly startTime: number
  private readonly endTime: number
  private readonly duration: number

  constructor(name: string, data: AnimationData[]) {
    this.name = name
    this.data = data
    this.startTime = Math.min(...data.map(({ times }) => times[0]))
    this.endTime = Math.max(...data.map(({ times }) => times[times.length - 1]))
    this.duration = this.endTime - this.startTime
  }

  public update(elapsed: number): void {
    elapsed = elapsed % this.duration
    elapsed = Math.min(elapsed, this.duration - EPS) + this.startTime

    this.data.forEach(({ node, transform, times, values }) => {
      const prevIndex = Math.max(1, times.findIndex((t) => t > elapsed)) - 1
      const nextIndex = prevIndex + 1
      const alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex])
      const size = values.length / times.length
      const prevVal = fromArray(values, prevIndex * size, size)
      const nextVal = fromArray(values, nextIndex * size, size)

      if (transform === "rotation") {
        node.rotation = q.slerp(prevVal as Quaternion, nextVal as Quaternion, alpha)
      } else if (transform === "position") {
        node.position = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      } else if (transform === "scale") {
        node.scale = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      }
      node.localMatrix = m4.compose(node.rotation, node.position, node.scale)
    })
  }
}


type AnimationData = {
  node: Object3d
  times: TypedArray
  values: TypedArray
  interpolation: AnimationInterpolationType
  transform: keyof Pick<Object3d, "position" | "rotation" | "scale">
}


public update(elapsed: number): void {
    elapsed = elapsed % this.duration
    elapsed = Math.min(elapsed, this.duration - EPS) + this.startTime

    this.data.forEach(({ node, transform, times, values }) => {
      const prevIndex = Math.max(1, times.findIndex((t) => t > elapsed)) - 1
      const nextIndex = prevIndex + 1
      const alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex])
      const size = values.length / times.length
      const prevVal = fromArray(values, prevIndex * size, size)
      const nextVal = fromArray(values, nextIndex * size, size)

      if (transform === "rotation") {
        node.rotation = q.slerp(prevVal as Quaternion, nextVal as Quaternion, alpha)
      } else if (transform === "position") {
        node.position = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      } else if (transform === "scale") {
        node.scale = v3.lerp(prevVal as Vector3, nextVal as Vector3, alpha)
      }
      node.localMatrix = m4.compose(node.rotation, node.position, node.scale)
    })
  }


// This is a little hacky. It can in principle mess stuff up if some other
// framework adds a :root CSS rule with --vars-that-end-with-color.
export default function useIntegerColors() {
  React.useEffect(() => {
    const newStyles: string[] = [];
    [...document.styleSheets].forEach(styleSheet => {
      [...(<any>styleSheet.cssRules)].forEach((rule: CSSStyleRule) => {
        if (rule.selectorText == ':root') {
          const style = rule.style;
          const newRuleLines: string[] = [];

          [...style].forEach(styleDeclaration => {
            const value = style.getPropertyValue(styleDeclaration).trim();
            const parsed = hexToRgb(value);
            if (styleDeclaration.startsWith('--') &&
                styleDeclaration.endsWith('-color') &&
                parsed != undefined)
            {
              const [r, g, b] = parsed;
              newRuleLines.push(
                `${styleDeclaration + '-integers'}: ` +
                parsed.map(n => n.toString()).join(', ') + ';');
            }
          });

          styleSheet.insertRule(
            ':root {\n' +
            newRuleLines.join('\n') +
            '}\n\n');
        }
      });
    });
  }, []);
}


function hexToRgb(hex: string): [number, number, number] | undefined {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? [
    parseInt(result[1], 16),
    parseInt(result[2], 16),
    parseInt(result[3], 16),
  ] : undefined;
}


() => {
    const newStyles: string[] = [];
    [...document.styleSheets].forEach(styleSheet => {
      [...(<any>styleSheet.cssRules)].forEach((rule: CSSStyleRule) => {
        if (rule.selectorText == ':root') {
          const style = rule.style;
          const newRuleLines: string[] = [];

          [...style].forEach(styleDeclaration => {
            const value = style.getPropertyValue(styleDeclaration).trim();
            const parsed = hexToRgb(value);
            if (styleDeclaration.startsWith('--') &&
                styleDeclaration.endsWith('-color') &&
                parsed != undefined)
            {
              const [r, g, b] = parsed;
              newRuleLines.push(
                `${styleDeclaration + '-integers'}: ` +
                parsed.map(n => n.toString()).join(', ') + ';');
            }
          });

          styleSheet.insertRule(
            ':root {\n' +
            newRuleLines.join('\n') +
            '}\n\n');
        }
      });
    });
  }


styleSheet => {
      [...(<any>styleSheet.cssRules)].forEach((rule: CSSStyleRule) => {
        if (rule.selectorText == ':root') {
          const style = rule.style;
          const newRuleLines: string[] = [];

          [...style].forEach(styleDeclaration => {
            const value = style.getPropertyValue(styleDeclaration).trim();
            const parsed = hexToRgb(value);
            if (styleDeclaration.startsWith('--') &&
                styleDeclaration.endsWith('-color') &&
                parsed != undefined)
            {
              const [r, g, b] = parsed;
              newRuleLines.push(
                `${styleDeclaration + '-integers'}: ` +
                parsed.map(n => n.toString()).join(', ') + ';');
            }
          });

          styleSheet.insertRule(
            ':root {\n' +
            newRuleLines.join('\n') +
            '}\n\n');
        }
      });
    }


(rule: CSSStyleRule) => {
        if (rule.selectorText == ':root') {
          const style = rule.style;
          const newRuleLines: string[] = [];

          [...style].forEach(styleDeclaration => {
            const value = style.getPropertyValue(styleDeclaration).trim();
            const parsed = hexToRgb(value);
            if (styleDeclaration.startsWith('--') &&
                styleDeclaration.endsWith('-color') &&
                parsed != undefined)
            {
              const [r, g, b] = parsed;
              newRuleLines.push(
                `${styleDeclaration + '-integers'}: ` +
                parsed.map(n => n.toString()).join(', ') + ';');
            }
          });

          styleSheet.insertRule(
            ':root {\n' +
            newRuleLines.join('\n') +
            '}\n\n');
        }
      }


styleDeclaration => {
            const value = style.getPropertyValue(styleDeclaration).trim();
            const parsed = hexToRgb(value);
            if (styleDeclaration.startsWith('--') &&
                styleDeclaration.endsWith('-color') &&
                parsed != undefined)
            {
              const [r, g, b] = parsed;
              newRuleLines.push(
                `${styleDeclaration + '-integers'}: ` +
                parsed.map(n => n.toString()).join(', ') + ';');
            }
          }


n => n.toString()


/**
 * The full definition of an event, with a string `type`.
 */
export interface EventObject {
  /**
   * The type of event that is sent.
   */
  type: string;
  /**
   * The unique ID that identifies this specific event instance.
   */
  id?: string | number;
  [other: string]: any;
}


/**
 * The full definition of an action, with a string `type` and an
 * `exec` implementation function.
 */
export interface ActionObject<TContext, TEvent extends EventObject> {
  /**
   * The type of action that is executed.
   */
  type: string;
  /**
   * The implementation for executing the action.
   */
  exec?: ActionFunction<TContext, TEvent>;
  [other: string]: any;
}


export interface ExecMeta<TContext, TEvent extends EventObject>
  extends StateMeta<TContext, TEvent> {
  action: ActionObject<TContext, TEvent>;
}


export interface ActionFunction<TContext, TEvent extends EventObject> {
  (context: TContext, event: TEvent, meta: ExecMeta<TContext, TEvent>):
    | any
    | void;
  name: string;
}


export interface StateValueMap {
  [key: string]: StateValue;
}


export interface HistoryValue {
  states: Record<string, HistoryValue | undefined>;
  current: StateValue | undefined;
}


export interface GuardPredicate<TContext, TEvent extends EventObject> {
  type: 'xstate.cond';
  predicate: ConditionPredicate<TContext, OmniEventObject<TEvent>>;
}


export interface GuardMeta<TContext, TEvent extends EventObject>
  extends StateMeta<TContext, TEvent> {
  cond: Guard<TContext, TEvent>;
}


export interface TransitionConfig<TContext, TEvent extends EventObject> {
  cond?: Condition<TContext, TEvent>;
  actions?: SingleOrArray<Action<TContext, TEvent>>;
  in?: StateValue;
  internal?: boolean;
  target?: string | string[];
}


export interface TargetTransitionConfig<TContext, TEvent extends EventObject>
  extends TransitionConfig<TContext, TEvent> {
  target: string | string[] | undefined;
}


export interface ActivityDefinition<TContext, TEvent extends EventObject>
  extends ActionObject<TContext, TEvent> {
  id: string;
  type: string;
}


export interface InvokeDefinition<TContext, TEvent extends EventObject>
  extends ActivityDefinition<TContext, TEvent> {
  /**
   * The source of the machine to be invoked, or the machine itself.
   */
  src: string;
  /**
   * If `true`, events sent to the parent service will be forwarded to the invoked service.
   *
   * Default: `false`
   */
  forward?: boolean;
  /**
   * Data from the parent machine's context to set as the (partial or full) context
   * for the invoked child machine.
   *
   * Data should be mapped to match the child machine's context shape.
   */
  data?: Mapper<TContext, TEvent> | PropertyMapper<TContext, TEvent>;
}


export interface Delay {
  id: string;
  /**
   * The time to delay the event, in milliseconds.
   */
  delay: number;
}


export interface StateNodeConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> {
  /**
   * The relative key of the state node, which represents its location in the overall state value.
   * This is automatically determined by the configuration shape via the key where it was defined.
   */
  key?: string;
  /**
   * The initial state node key.
   */
  initial?: keyof TStateSchema['states'] | undefined;
  /**
   * @deprecated
   */
  parallel?: boolean | undefined;
  /**
   * The type of this state node:
   *
   *  - `'atomic'` - no child state nodes
   *  - `'compound'` - nested child state nodes (XOR)
   *  - `'parallel'` - orthogonal nested child state nodes (AND)
   *  - `'history'` - history state node
   *  - `'final'` - final state node
   */
  type?: StateTypes;
  /**
   * The initial context (extended state) of the machine.
   */
  context?: TContext;
  /**
   * Indicates whether the state node is a history state node, and what
   * type of history:
   * shallow, deep, true (shallow), false (none), undefined (none)
   */
  history?: 'shallow' | 'deep' | boolean | undefined;
  /**
   * The mapping of state node keys to their state node configurations (recursive).
   */
  states?: StatesConfig<TContext, TStateSchema, TEvent> | undefined;
  /**
   * The services to invoke upon entering this state node. These services will be stopped upon exiting this state node.
   */
  invoke?: InvokesConfig<TContext, TEvent>;
  /**
   * The mapping of event types to their potential transition(s).
   */
  on?: TransitionsConfig<TContext, TEvent>;
  /**
   * The action(s) to be executed upon entering the state node.
   */
  onEntry?: SingleOrArray<Action<TContext, TEvent>>;
  /**
   * The action(s) to be executed upon exiting the state node.
   */
  onExit?: SingleOrArray<Action<TContext, TEvent>>;
  /**
   * The potential transition(s) to be taken upon reaching a final child state node.
   *
   * This is equivalent to defining a `[done(id)]` transition on this state node's `on` property.
   */
  onDone?: string | SingleOrArray<TransitionConfig<TContext, DoneEventObject>>;
  /**
   * The mapping (or array) of delays (in milliseconds) to their potential transition(s).
   * The delayed transitions are taken after the specified delay in an interpreter.
   */
  after?: DelayedTransitions<TContext, TEvent>;
  /**
   * The activities to be started upon entering the state node,
   * and stopped upon exiting the state node.
   */
  activities?: SingleOrArray<Activity<TContext, TEvent>>;
  /**
   * @private
   */
  parent?: StateNode<TContext>;
  strict?: boolean | undefined;
  /**
   * The meta data associated with this state node, which will be returned in State instances.
   */
  meta?: TStateSchema extends { meta: infer D } ? D : any;
  /**
   * The data sent with the "done.state._id_" event if this is a final state node.
   *
   * The data will be evaluated with the current `context` and placed on the `.data` property
   * of the event.
   */
  data?: Mapper<TContext, TEvent> | PropertyMapper<TContext, TEvent>;
  /**
   * The unique ID of the state node, which can be referenced as a transition target via the
   * `#id` syntax.
   */
  id?: string | undefined;
  /**
   * The string delimiter for serializing the path to a string. The default is "."
   */
  delimiter?: string;
  /**
   * The order this state node appears. Corresponds to the implicit SCXML document order.
   */
  order?: number;
}


export interface StateNodeDefinition<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends StateNodeConfig<TContext, TStateSchema, TEvent> {
  id: string;
  version: string | undefined;
  key: string;
  type: StateTypes;
  initial: StateNodeConfig<TContext, TStateSchema, TEvent>['initial'];
  history: boolean | 'shallow' | 'deep' | undefined;
  states: StatesDefinition<TContext, TStateSchema, TEvent>;
  on: TransitionsDefinition<TContext, TEvent>;
  onEntry: Array<ActionObject<TContext, TEvent>>;
  onExit: Array<ActionObject<TContext, TEvent>>;
  activities: Array<ActivityDefinition<TContext, TEvent>>;
  meta: any;
  order: number;
  data?: FinalStateNodeConfig<TContext, TEvent>['data'];
}


export interface AtomicStateNodeConfig<TContext, TEvent extends EventObject>
  extends StateNodeConfig<TContext, StateSchema, TEvent> {
  initial?: undefined;
  parallel?: false | undefined;
  states?: undefined;
  onDone?: undefined;
}


export interface HistoryStateNodeConfig<TContext, TEvent extends EventObject>
  extends AtomicStateNodeConfig<TContext, TEvent> {
  history: 'shallow' | 'deep' | true;
  target: StateValue | undefined;
}


export interface FinalStateNodeConfig<TContext, TEvent extends EventObject>
  extends AtomicStateNodeConfig<TContext, TEvent> {
  type: 'final';
  /**
   * The data to be sent with the "done.state.<id>" event. The data can be
   * static or dynamic (based on assigners).
   */
  data?: Assigner<TContext, TEvent> | PropertyAssigner<TContext, TEvent> | any;
}


export interface CompoundStateNodeConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends StateNodeConfig<TContext, TStateSchema, TEvent> {
  parallel?: boolean;
  states: StateNodeConfig<TContext, TStateSchema, TEvent>['states'];
}


export interface MachineOptions<TContext, TEvent extends EventObject> {
  guards: Record<string, ConditionPredicate<TContext, TEvent>>;
  actions: ActionFunctionMap<TContext, TEvent>;
  activities: Record<string, ActivityConfig<TContext, TEvent>>;
  services: Record<string, ServiceConfig<TContext>>;
  delays: Record<string, DelayConfig<TContext, TEvent>>;
  updater: Updater<TContext, TEvent>;
}


export interface MachineConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends CompoundStateNodeConfig<TContext, TStateSchema, TEvent> {
  /**
   * The initial context (extended state)
   */
  context?: TContext;
  /**
   * The machine's own version.
   */
  version?: string;
}


export interface StandardMachineConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends CompoundStateNodeConfig<TContext, TStateSchema, TEvent> {}


export interface ParallelMachineConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends CompoundStateNodeConfig<TContext, TStateSchema, TEvent> {
  initial?: undefined;
  type?: 'parallel';
}


export interface EntryExitEffectMap<TContext, TEvent extends EventObject> {
  entry: Array<ActionObject<TContext, TEvent>>;
  exit: Array<ActionObject<TContext, TEvent>>;
}


export interface HistoryStateNode<TContext> extends StateNode<TContext> {
  history: 'shallow' | 'deep';
  target: StateValue | undefined;
}


export interface StateMachine<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> extends StateNode<TContext, TStateSchema, TEvent> {
  id: string;
  states: StateNode<TContext, TStateSchema, TEvent>['states'];
}


export interface ActionMap<TContext, TEvent extends EventObject> {
  onEntry: Array<Action<TContext, TEvent>>;
  actions: Array<Action<TContext, TEvent>>;
  onExit: Array<Action<TContext, TEvent>>;
}


export interface EntryExitStates<TContext> {
  entry: Set<StateNode<TContext>>;
  exit: Set<StateNode<TContext>>;
}


export interface EntryExitStateArrays<TContext> {
  entry: Array<StateNode<TContext>>;
  exit: Array<StateNode<TContext>>;
}


export interface ActivityMap {
  [activityKey: string]: boolean;
}


// tslint:disable-next-line:class-name
export interface StateTransition<TContext, TEvent extends EventObject> {
  tree: StateTree | undefined;
  /**
   * The source state that preceded the transition.
   */
  source: State<TContext> | undefined;
  reentryStates: Set<StateNode<TContext>> | undefined;
  actions: Array<ActionObject<TContext, TEvent>>;
}


export interface TransitionData<TContext, TEvent extends EventObject> {
  value: StateValue | undefined;
  actions: ActionMap<TContext, TEvent>;
  activities?: ActivityMap;
}


export interface RaisedEvent<TEvent extends EventObject> {
  type: ActionTypes.Raise;
  event: TEvent;
}


export interface RaiseEvent<TContext, TEvent extends EventObject>
  extends ActionObject<TContext, TEvent> {
  event: Event<TEvent>;
}


export interface DoneInvokeEvent<TData> extends EventObject {
  data: TData;
}


export interface ErrorExecutionEvent extends EventObject {
  src: string;
  type: ActionTypes.ErrorExecution;
  data: any;
}


export interface DoneEventObject extends EventObject {
  data?: any;
  toString(): string;
}


export interface ActivityActionObject<TContext, TEvent extends EventObject>
  extends ActionObject<TContext, TEvent> {
  type: ActionTypes.Start | ActionTypes.Stop;
  activity: ActivityDefinition<TContext, TEvent>;
  exec: ActionFunction<TContext, TEvent> | undefined;
}


export interface SendAction<TContext, TEvent extends EventObject>
  extends ActionObject<TContext, TEvent> {
  to: string | undefined;
  event: TEvent | SendExpr<TContext, TEvent>; // TODO: use Expr type
  delay?: number | string | Expr<TContext, TEvent, number>;
  id: string | number;
}


export interface SendActionObject<TContext, TEvent extends EventObject>
  extends SendAction<TContext, TEvent> {
  to: string | undefined;
  event: TEvent;
  delay?: number | string;
  id: string | number;
}


export interface SendActionOptions<TContext, TEvent extends EventObject> {
  id?: string | number;
  delay?: number | string | Expr<TContext, TEvent, number>;
  to?: string;
}


export interface CancelAction extends ActionObject<any, any> {
  sendId: string | number;
}


export interface AssignAction<TContext, TEvent extends EventObject>
  extends ActionObject<TContext, TEvent> {
  assignment: Assigner<TContext, TEvent> | PropertyAssigner<TContext, TEvent>;
}


export interface TransitionDefinition<TContext, TEvent extends EventObject>
  extends TransitionConfig<TContext, TEvent> {
  actions: Array<ActionObject<TContext, TEvent>>;
  cond?: Guard<TContext, TEvent>;
  event: string;
}


export interface DelayedTransitionDefinition<
  TContext,
  TEvent extends EventObject
> extends TransitionDefinition<TContext, TEvent> {
  delay: number | string | Expr<TContext, TEvent, number>;
}


export interface Edge<
  TContext,
  TEvent extends EventObject,
  TEventType extends TEvent['type'] = string
> {
  event: TEventType;
  source: StateNode<TContext>;
  target: StateNode<TContext>;
  cond?: Condition<TContext, TEvent & { type: TEventType }>;
  actions: Array<Action<TContext, TEvent>>;
  meta?: MetaObject;
  transition: TransitionDefinition<TContext, TEvent>;
}


export interface NodesAndEdges<TContext, TEvent extends EventObject> {
  nodes: StateNode[];
  edges: Array<Edge<TContext, TEvent, TEvent['type']>>;
}


export interface Segment<TContext, TEvent extends EventObject> {
  /**
   * From state.
   */
  state: {
    value: StateValue;
    context: TContext;
  };
  /**
   * Event from state.
   */
  event: TEvent;
}


export interface PathMap<TContext, TEvent extends EventObject> {
  [key: string]: Array<Segment<TContext, TEvent>>;
}


export interface PathItem<TContext, TEvent extends EventObject> {
  state: {
    value: StateValue;
    context: TContext;
  };
  path: Array<Segment<TContext, TEvent>>;
}


export interface PathsItem<TContext, TEvent extends EventObject> {
  state: {
    value: StateValue;
    context: TContext;
  };
  paths: Array<Array<Segment<TContext, TEvent>>>;
}


export interface PathsMap<TContext, TEvent extends EventObject> {
  [key: string]: PathsItem<TContext, TEvent>;
}


export interface TransitionMap {
  state: StateValue | undefined;
}


export interface AdjacencyMap {
  [stateId: string]: Record<string, TransitionMap>;
}


export interface ValueAdjacencyMap<TContext, TEvent extends EventObject> {
  [stateId: string]: Record<string, State<TContext, TEvent>>;
}


export interface StateMeta<TContext, TEvent extends EventObject> {
  state: State<TContext, TEvent>;
}


export interface StateInterface<
  TContext = DefaultContext,
  TEvent extends EventObject = EventObject
> {
  value: StateValue;
  tree?: StateTree;
  history?: State<TContext>;
  historyValue?: HistoryValue | undefined;
  actions: Array<ActionObject<TContext, TEvent>>;
  activities: ActivityMap;
  meta: any;
  events: TEvent[];
  context: TContext;
  toStrings: () => string[];
}


export interface StateConfig<TContext, TEvent extends EventObject> {
  value: StateValue;
  context: TContext;
  event: OmniEventObject<TEvent>;
  historyValue?: HistoryValue | undefined;
  history?: State<TContext>;
  actions?: Array<ActionObject<TContext, TEvent>>;
  activities?: ActivityMap;
  meta?: any;
  events?: TEvent[];
  tree?: StateTree;
}


export interface StateSchema {
  meta?: any;
  states?: Record<string | number, StateSchema>;
}


export interface InterpreterOptions {
  /**
   * Whether state actions should be executed immediately upon transition. Defaults to `true`.
   */
  execute: boolean;
  clock: Clock;
  logger: (...args: any[]) => void;
  parent?: Interpreter<any, any, any>;
  /**
   * If `true`, defers processing of sent events until the service
   * is initialized (`.start()`). Otherwise, an error will be thrown
   * for events sent to an uninitialized service.
   *
   * Default: `true`
   */
  deferEvents: boolean;
  /**
   * The custom `id` for referencing this service.
   */
  id?: string;
  /**
   * If `true`, states and events will be logged to Redux DevTools.
   *
   * Default: `false`
   */
  devTools: boolean;
}


export enum ActionTypes {
  Start = 'xstate.start',
  Stop = 'xstate.stop',
  Raise = 'xstate.raise',
  Send = 'xstate.send',
  Cancel = 'xstate.cancel',
  NullEvent = '',
  Assign = 'xstate.assign',
  After = 'xstate.after',
  DoneState = 'done.state',
  DoneInvoke = 'done.invoke',
  Log = 'xstate.log',
  Init = 'xstate.init',
  Invoke = 'xstate.invoke',
  ErrorExecution = 'error.execution',
  ErrorCommunication = 'error.communication'
}


export enum SpecialTargets {
  Parent = '#_parent',
  Internal = '#_internal'
}


export type EventType = string;


export type ActionType = string;


export type MetaObject = Record<string, any>;


export type DefaultContext = Record<string, any> | undefined;


/**
 * The specified string event types or the specified event objects.
 */
export type Event<TEvent extends EventObject> = TEvent['type'] | TEvent;


/**
 * Represents the specified event types or the full event objects,
 * as well as the built in event types and/or objects.
 */
export type OmniEvent<TEvent extends EventObject> =
  | TEvent['type']
  | BuiltInEvent<TEvent>['type']
  | OmniEventObject<TEvent>;


// export type InternalAction<TContext> = SendAction | AssignAction<TContext>;
export type Action<TContext, TEvent extends EventObject> =
  | ActionType
  | ActionObject<TContext, TEvent>
  | ActionFunction<TContext, TEvent>;


export type StateKey = string | State<any>;


/**
 * The string or object representing the state value relative to the parent state node.
 *
 * - For a child atomic state node, this is a string, e.g., `"pending"`.
 * - For complex state nodes, this is an object, e.g., `{ success: "someChildState" }`.
 */
export type StateValue = string | StateValueMap;


export type ConditionPredicate<TContext, TEvent extends EventObject> = (
  context: TContext,
  event: TEvent,
  meta: GuardMeta<TContext, TEvent>
) => boolean;


export type Guard<TContext, TEvent extends EventObject> =
  | GuardPredicate<TContext, TEvent>
  | Record<string, any> & {
      type: string;
    };


export type Condition<TContext, TEvent extends EventObject> =
  | string
  | ConditionPredicate<TContext, TEvent>
  | Guard<TContext, TEvent>;


export type ConditionalTransitionConfig<
  TContext,
  TEvent extends EventObject = EventObject
> = Array<TransitionConfig<TContext, TEvent>>;


export type Transition<TContext, TEvent extends EventObject = EventObject> =
  | string
  | TransitionConfig<TContext, TEvent>
  | ConditionalTransitionConfig<TContext, TEvent>;


export type DisposeActivityFunction = () => void;


export type ActivityConfig<TContext, TEvent extends EventObject> = (
  ctx: TContext,
  activity: ActivityDefinition<TContext, TEvent>
) => DisposeActivityFunction | void;


export type Activity<TContext, TEvent extends EventObject> =
  | string
  | ActivityDefinition<TContext, TEvent>;


export type Sender<TEvent extends EventObject> = (event: Event<TEvent>) => void;


export type Receiver<TEvent extends EventObject> = (
  listener: (event: TEvent) => void
) => void;


export type InvokeCallback = ((
  sender: Sender<any>,
  onEvent: Receiver<EventObject>
) => any);


/**
 * Returns either a Promises or a callback handler (for streams of events) given the
 * machine's current `context` and `event` that invoked the service.
 *
 * For Promises, the only events emitted to the parent will be:
 * - `done.invoke.<id>` with the `data` containing the resolved payload when the promise resolves, or:
 * - `error.execution` with the `data` containing the caught error, and `src` containing the service `id`.
 *
 * For callback handlers, the `sender` will be provided, which will send events to the parent service.
 *
 * @param context The current machine `context`
 * @param event The event that invoked the service
 */
export type InvokeCreator<TFinalContext, TContext> = (
  context: TContext,
  event: EventObject
) =>
  | Promise<TFinalContext>
  | StateMachine<TFinalContext, any, any>
  | InvokeCallback;


export type DelayedTransitions<TContext, TEvent extends EventObject> =
  | Record<
      string | number,
      string | SingleOrArray<TransitionConfig<TContext, TEvent>>
    >
  | Array<
      TransitionConfig<TContext, TEvent> & {
        delay: number | string | Expr<TContext, TEvent, number>;
      }
    >;


export type StateTypes =
  | 'atomic'
  | 'compound'
  | 'parallel'
  | 'final'
  | 'history'
  | string;


export type SingleOrArray<T> = T[] | T;


export type StateNodesConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> = {
  [K in keyof TStateSchema['states']]: StateNode<
    TContext,
    TStateSchema['states'][K],
    TEvent
  >
};


export type StatesConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> = {
  [K in keyof TStateSchema['states']]: StateNodeConfig<
    TContext,
    TStateSchema['states'][K],
    TEvent
  >
};


export type StatesDefinition<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> = {
  [K in keyof TStateSchema['states']]: StateNodeDefinition<
    TContext,
    TStateSchema['states'][K],
    TEvent
  >
};


export type TransitionsConfig<TContext, TEvent extends EventObject> = {
  [K in TEvent['type'] | BuiltInEvent<TEvent>['type']]?:
    | string
    | number
    | StateNode<TContext>
    | SingleOrArray<TransitionConfig<TContext, Extract<TEvent, { type: K }>>>
};


export type TransitionsDefinition<TContext, TEvent extends EventObject> = {
  [K in TEvent['type']]: Array<
    TransitionDefinition<TContext, Extract<TEvent, { type: K }>>
  >
};


export type InvokeConfig<TContext, TEvent extends EventObject> =
  | {
      /**
       * The unique identifier for the invoked machine. If not specified, this
       * will be the machine's own `id`, or the URL (from `src`).
       */
      id?: string;
      /**
       * The source of the machine to be invoked, or the machine itself.
       */
      src: string | StateMachine<any, any, any> | InvokeCreator<any, TContext>;
      /**
       * If `true`, events sent to the parent service will be forwarded to the invoked service.
       *
       * Default: `false`
       */
      forward?: boolean;
      /**
       * Data from the parent machine's context to set as the (partial or full) context
       * for the invoked child machine.
       *
       * Data should be mapped to match the child machine's context shape.
       */
      data?: Mapper<TContext, TEvent> | PropertyMapper<TContext, TEvent>;
      /**
       * The transition to take upon the invoked child machine reaching its final top-level state.
       */
      onDone?:
        | string
        | SingleOrArray<TransitionConfig<TContext, DoneInvokeEvent<any>>>;
      /**
       * The transition to take upon the invoked child machine sending an error event.
       */
      onError?:
        | string
        | SingleOrArray<TransitionConfig<TContext, DoneInvokeEvent<any>>>;
    }
  | StateMachine<any, any, any>;


export type InvokesConfig<TContext, TEvent extends EventObject> = SingleOrArray<
  InvokeConfig<TContext, TEvent>
>;


export type AnyStateNodeDefinition = StateNodeDefinition<any, any, any>;


export type SimpleOrCompoundStateNodeConfig<
  TContext,
  TStateSchema extends StateSchema,
  TEvent extends EventObject
> =
  | AtomicStateNodeConfig<TContext, TEvent>
  | CompoundStateNodeConfig<TContext, TStateSchema, TEvent>;


export type ActionFunctionMap<TContext, TEvent extends EventObject> = Record<
  string,
  ActionObject<TContext, TEvent> | ActionFunction<TContext, TEvent>
>;


export type ServiceConfig<TContext> =
  | string
  | StateMachine<any, any, any>
  | InvokeCreator<any, TContext>;


export type DelayConfig<TContext, TEvent extends EventObject> =
  | number
  | Expr<TContext, OmniEventObject<TEvent>, number>;


export type DoneEvent = DoneEventObject & string;


export type BuiltInEvent<TEvent extends EventObject> =
  | { type: ActionTypes.NullEvent }
  | { type: ActionTypes.Init }
  | RaisedEvent<TEvent>
  | ErrorExecutionEvent;


/**
 * Represents the specified events and the built-in internal events.
 */
export type OmniEventObject<TEvent extends EventObject> =
  | TEvent
  | BuiltInEvent<TEvent>;


export type Expr<TContext, TEvent extends EventObject, T> = (
  context: TContext,
  event: TEvent
) => T;


export type SendExpr<TContext, TEvent extends EventObject> = (
  context: TContext,
  event: TEvent
) => OmniEvent<TEvent>;


export type Assigner<TContext, TEvent extends EventObject> = (
  extState: TContext,
  event: TEvent
) => Partial<TContext>;


export type PropertyAssigner<TContext, TEvent extends EventObject> = Partial<
  {
    [K in keyof TContext]:
      | ((extState: TContext, event: TEvent) => TContext[K])
      | TContext[K]
  }
>;


export type Mapper<TContext, TEvent extends EventObject> = (
  extState: TContext,
  event: TEvent
) => any;


export type PropertyMapper<TContext, TEvent extends EventObject> = Partial<{
  [key: string]: ((extState: TContext, event: TEvent) => any) | any;
}>;


export type Updater<TContext, TEvent extends EventObject> = (
  context: TContext,
  event: OmniEventObject<TEvent>,
  assignActions: Array<AssignAction<TContext, TEvent>>
) => TContext;


/*
 * Copyright (c) 2018 THL A29 Limited, a Tencent company. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/**
 * 账号风险检测结果
 */
export interface RiskDetails {
  /**
   * 该字段用于返回账号信息检测对应的风险类别，取值为：**RiskAccount**（账号存在风险）、**RiskIP**（IP地址存在风险）、**RiskIMEI**（移动设备识别码存在风险）。
   */
  Label: string

  /**
   * 该字段用于返回账号信息检测对应的风险等级，取值为：**1**（疑似存在风险）和**2**（存在恶意风险）。
   */
  Level: number
}


/**
 * TextModeration请求参数结构体
 */
export interface TextModerationRequest {
  /**
   * 文本内容Base64编码，限制原文长度不能超过10000个unicode字符
   */
  Content: string

  /**
   * 该字段表示策略的具体编号，用于接口调度，在内容安全控制台中可配置。若不传入Biztype参数（留空），则代表采用默认的识别策略；传入则会在审核时根据业务场景采取不同的审核策略。
备注：Biztype仅为数字、字母与下划线的组合，长度为3-32个字符；不同Biztype关联不同的业务场景与识别能力策略，调用前请确认正确的Biztype
   */
  BizType?: string

  /**
   * 该字段表示您为待检测对象分配的数据ID，传入后可方便您对文件进行标识和管理。
取值：由英文字母（大小写均可）、数字及四个特殊符号（_，-，@，#）组成，**长度不超过64个字符**
   */
  DataId?: string

  /**
   * 该字段表示待检测对象对应的用户相关信息，传入后可便于甄别相应违规风险用户
   */
  User?: User

  /**
   * 该字段表示待检测对象对应的设备相关信息，传入后可便于甄别相应违规风险设备
   */
  Device?: Device
}


/**
 * 文本审核返回的详细结果
 */
export interface DetailResults {
  /**
   * 该字段用于返回检测结果所对应的全部恶意标签。
返回值：**Normal**：正常，**Porn**：色情，**Abuse**：谩骂，**Ad**：广告，**Custom**：自定义违规；以及其他令人反感、不安全或不适宜的内容类型。
   */
  Label: string

  /**
      * 该字段用于返回对应当前标签的后续操作建议。当您获取到判定结果后，返回值表示系统推荐的后续操作；建议您按照业务所需，对不同违规类型与建议值进行处理。
返回值：**Block**：建议屏蔽，**Review** ：建议人工复审，**Pass**：建议通过
注意：此字段可能返回 null，表示取不到有效值。
      */
  Suggestion: string

  /**
      * 该字段用于返回检测文本命中的关键词信息，用于标注文本违规的具体原因（如：*加我微信*）。该参数可能会有多个返回值，代表命中的多个关键词；如返回值为空且Score不为空，则代表识别结果所对应的恶意标签（Label）是来自于语义模型判断的返回值。
注意：此字段可能返回 null，表示取不到有效值。
      */
  Keywords: Array<string>

  /**
      * 该字段用于返回当前标签（Label）下的置信度，取值范围：0（**置信度最低**）-100（**置信度最高** ），越高代表文本越有可能属于当前返回的标签；如：*色情 99*，则表明该文本非常有可能属于色情内容；*色情 0*，则表明该文本不属于色情内容。
注意：此字段可能返回 null，表示取不到有效值。
      */
  Score: number

  /**
      * 该字段**仅当Label为Custom自定义关键词时有效**，用于返回自定义关键词对应的词库类型，取值为**1**（黑白库）和**2**（自定义关键词库），若未配置自定义关键词库,则默认值为1（黑白库匹配）。
注意：此字段可能返回 null，表示取不到有效值。
      */
  LibType: number

  /**
      * 该字段**仅当Label为Custom：自定义关键词时该参数有效**,用于返回自定义库的ID，以方便自定义库管理和配置。
注意：此字段可能返回 null，表示取不到有效值。
      */
  LibId: string

  /**
      * 该字段**仅当Label为Custom：自定义关键词时该参数有效**,用于返回自定义库的名称,以方便自定义库管理和配置。
注意：此字段可能返回 null，表示取不到有效值。
      */
  LibName: string
}


/**
 * 用于表示业务用户的账号相关信息
 */
export interface User {
  /**
      * 该字段表示业务用户ID,填写后，系统可根据账号过往违规历史优化审核结果判定，有利于存在可疑违规风险时的辅助判断。

备注：该字段可传入微信openid、QQopenid、字符串等账号信息，与账号类别参数（AccountType）配合使用可确定唯一账号。
      */
  UserId?: string

  /**
   * 该字段表示业务用户对应的账号昵称信息。
   */
  Nickname?: string

  /**
      * 该字段表示业务用户ID对应的账号类型，取值：**1**-微信uin，**2**-QQ号，**3**-微信群uin，**4**-qq群号，**5**-微信openid，**6**-QQopenid，**7**-其它string。

该字段与账号ID参数（UserId）配合使用可确定唯一账号。
      */
  AccountType?: number

  /**
      * 该字段表示业务用户对应账号的性别信息。

取值：**0**（默认值，代表性别未知）、**1**（男性）、**2**（女性）。
      */
  Gender?: number

  /**
      * 该字段表示业务用户对应账号的年龄信息。

取值：**0**（默认值，代表年龄未知）-（**自定义年龄上限**）之间的整数。
      */
  Age?: number

  /**
      * 该字段表示业务用户对应账号的等级信息。

取值：**0**（默认值，代表等级未知）、**1**（等级较低）、**2**（等级中等）、**3**（等级较高），目前**暂不支持自定义等级**。
      */
  Level?: number

  /**
      * 该字段表示业务用户对应账号的手机号信息，支持全球各地区手机号的记录。

备注：请保持手机号格式的统一，如区号格式（086/+86）等。
      */
  Phone?: string

  /**
      * 该字段表示业务用户头像图片的访问链接(URL)，支持PNG、JPG、JPEG、BMP、GIF、WEBP格式。
备注：头像图片大小不超过5MB，建议分辨率不低于256x256；图片下载时间限制为3秒，超过则会返回下载超时。
      */
  HeadUrl?: string

  /**
   * 该字段表示业务用户的简介信息，支持汉字、英文及特殊符号，长度不超过5000个汉字字符。
   */
  Desc?: string
}


/**
 * 用于表示业务用户对应的设备信息
 */
export interface Device {
  /**
      * 该字段表示业务用户对应设备的IP地址。

备注:目前仅支持IPv4地址记录，不支持IPv6地址记录。
      */
  IP?: string

  /**
   * 该字段表示业务用户对应的MAC地址，以方便设备识别与管理；其格式与取值与标准MAC地址一致。
   */
  Mac?: string

  /**
   * *内测中，敬请期待。*
   */
  TokenId?: string

  /**
   * *内测中，敬请期待。*
   */
  DeviceId?: string

  /**
   * 该字段表示业务用户对应设备的**IMEI码**（国际移动设备识别码），该识别码可用于识别每一部独立的手机等移动通信设备，方便设备识别与管理。
备注：格式为**15-17位纯数字**。
   */
  IMEI?: string

  /**
      * **iOS设备专用**，该字段表示业务用户对应的**IDFA**(广告标识符),这是由苹果公司提供的用于追踪用户的广告标识符，由一串16进制的32位数字和字母组成。

备注：苹果公司自2021年iOS14更新后允许用户手动关闭或者开启IDFA，故此字符串标记有效性可能有所降低。
      */
  IDFA?: string

  /**
   * **iOS设备专用**，该字段表示业务用户对应的**IDFV**(应用开发商标识符),这是由苹果公司提供的用于标注应用开发商的标识符，由一串16进制的32位数字和字母组成，可被用于唯一标识设备。
   */
  IDFV?: string
}


/**
 * TextModeration返回参数结构体
 */
export interface TextModerationResponse {
  /**
   * 该字段用于返回请求参数中的BizType参数
   */
  BizType: string

  /**
   * 该字段用于返回检测结果（DetailResults）中所对应的**优先级最高的恶意标签**，表示模型推荐的审核结果，建议您按照业务所需，对不同违规类型与建议值进行处理。
返回值：**Normal**：正常，**Porn**：色情，**Abuse**：谩骂，**Ad**：广告，**Custom**：自定义违规；以及其他令人反感、不安全或不适宜的内容类型
   */
  Label: string

  /**
   * 该字段用于返回后续操作建议。当您获取到判定结果后，返回值表示系统推荐的后续操作；建议您按照业务所需，对不同违规类型与建议值进行处理。
返回值：**Block**：建议屏蔽，**Review** ：建议人工复审，**Pass**：建议通过
   */
  Suggestion: string

  /**
      * 该字段用于返回当前标签（Label）下被检测文本命中的关键词信息，用于标注文本违规的具体原因（如：*加我微信*）。该参数可能会有多个返回值，代表命中的多个关键词；如返回值为空且Score不为空，则代表识别结果所对应的恶意标签（Label）是来自于语义模型判断的返回值
注意：此字段可能返回 null，表示取不到有效值。
      */
  Keywords: Array<string>

  /**
   * 该字段用于返回当前标签（Label）下的置信度，取值范围：0（**置信度最低**）-100（**置信度最高** ），越高代表文本越有可能属于当前返回的标签；如：*色情 99*，则表明该文本非常有可能属于色情内容；*色情 0*，则表明该文本不属于色情内容
   */
  Score: number

  /**
      * 该字段用于返回基于文本风险库审核的详细结果，返回值信息可参阅对应数据结构（DetailResults）的详细描述
注意：此字段可能返回 null，表示取不到有效值。
      */
  DetailResults: Array<DetailResults>

  /**
      * 该字段用于返回文本检测中存在违规风险的账号检测结果，主要包括违规风险类别和风险等级信息，具体内容可参阅对应数据结构（RiskDetails）的详细描述
注意：此字段可能返回 null，表示取不到有效值。
      */
  RiskDetails: Array<RiskDetails>

  /**
      * 该字段用于返回根据您的需求配置的额外附加信息（Extra），如未配置则默认返回值为空。
备注：不同客户或Biztype下返回信息不同，如需配置该字段请提交工单咨询或联系售后专员处理
注意：此字段可能返回 null，表示取不到有效值。
      */
  Extra: string

  /**
      * 该字段用于返回检测对象对应请求参数中的DataId，与输入的DataId字段中的内容对应
注意：此字段可能返回 null，表示取不到有效值。
      */
  DataId: string

  /**
   * 唯一请求 ID，每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
   */
  RequestId?: string
}


(theme) => ({
  selectGrid: {
    display: 'grid',
    gridTemplateColumns: 'repeat(3, 1fr)',
    gridGap: theme.spacing(1),
    [theme.breakpoints.down('sm')]: {
      gridTemplateColumns: '1fr',
    },
  },
  gutterTop: {
    marginTop: theme.spacing(2),
  },
})


({ isAdmin, user }: ProfileSettingsProps) => {
  const classes = useStyles();
  const { event } = useGoogleAnalytics();
  const showSnackbar = useSnackbar();
  const updateUser = useUpdateUser();
  const { register, handleSubmit, formState, control, setValue, watch } = useForm<FormData>({ defaultValues: { ...user } });
  const updateData = (data: FormData) => {
    if (updateUser.isLoading) {
      return;
    }
    updateUser.mutate(
      { userId: user.user_id, user: { ...user, ...data } },
      {
        onSuccess: () => {
          showSnackbar('Bruker oppdatert', 'success');
          event('update-settings', 'profile', 'Update');
        },
        onError: (e) => {
          showSnackbar(e.detail, 'error');
        },
      },
    );
  };

  if (!user) {
    return null;
  } else {
    return (
      <form onSubmit={handleSubmit(updateData)}>
        {isAdmin && (
          <div className={classes.selectGrid}>
            <TextField disabled={updateUser.isLoading} formState={formState} label='Fornavn' {...register('first_name')} />
            <TextField disabled={updateUser.isLoading} formState={formState} label='Etternavn' {...register('last_name')} />
            <TextField disabled={updateUser.isLoading} formState={formState} label='Epost' {...register('email')} />
          </div>


(data: FormData) => {
    if (updateUser.isLoading) {
      return;
    }
    updateUser.mutate(
      { userId: user.user_id, user: { ...user, ...data } },
      {
        onSuccess: () => {
          showSnackbar('Bruker oppdatert', 'success');
          event('update-settings', 'profile', 'Update');
        },
        onError: (e) => {
          showSnackbar(e.detail, 'error');
        },
      },
    );
  }


() => {
          showSnackbar('Bruker oppdatert', 'success');
          event('update-settings', 'profile', 'Update');
        }


(e) => {
          showSnackbar(e.detail, 'error');
        }


(i) => (
              <MenuItem key={i}


export type ProfileSettingsProps = {
  user: UserList;
  isAdmin?: boolean;
};


type FormData = Pick<UserList, 'first_name' | 'last_name' | 'email' | 'cell' | 'image' | 'gender' | 'allergy' | 'tool' | 'user_class' | 'user_study'>;


handleSubmit(updateData)


register('image')


getUserStudyLong(i)


getUserClass(i)


/**
 * @internal
 */
export function neutralFillStealth(
    palette: Palette,
    reference: Swatch,
    restDelta: number,
    hoverDelta: number,
    activeDelta: number,
    focusDelta: number,
    fillRestDelta: number,
    fillHoverDelta: number,
    fillActiveDelta: number,
    fillFocusDelta: number
): InteractiveSwatchSet {
    const swapThreshold = Math.max(
        restDelta,
        hoverDelta,
        activeDelta,
        focusDelta,
        fillRestDelta,
        fillHoverDelta,
        fillActiveDelta,
        fillFocusDelta
    );

    const referenceIndex = palette.closestIndexOf(reference);
    const direction: 1 | -1 = referenceIndex >= swapThreshold ? -1 : 1;

    return {
        rest: palette.get(referenceIndex + direction * restDelta),
        hover: palette.get(referenceIndex + direction * hoverDelta),
        active: palette.get(referenceIndex + direction * activeDelta),
        focus: palette.get(referenceIndex + direction * focusDelta),
    };
}


(error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      throw(error);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    }


@Injectable({
  providedIn: 'root',
})
export class DownloadService {

  private url = '/api/download';

  constructor(private http: HttpClient) { }

  listaDePresenca(turmaId: number): Observable<Blob> {
    const headerOptions = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/pdf'
    });
    const requestOptions = {headers : headerOptions, responseType: 'blob' as 'blob'};
    return this.http.get(`${this.url}/listaDePresenca/${turmaId}`, requestOptions);
  }

  atestado(alunoId: number): Observable<Blob> {
    const headerOptions = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/pdf'
    });
    const requestOptions = {headers : headerOptions, responseType: 'blob' as 'blob'};
    return this.http.get(`${this.url}/atestado/${alunoId}`, requestOptions);
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      throw(error);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }
}


listaDePresenca(turmaId: number): Observable<Blob> {
    const headerOptions = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/pdf'
    });
    const requestOptions = {headers : headerOptions, responseType: 'blob' as 'blob'};
    return this.http.get(`${this.url}/listaDePresenca/${turmaId}`, requestOptions);
  }


atestado(alunoId: number): Observable<Blob> {
    const headerOptions = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/pdf'
    });
    const requestOptions = {headers : headerOptions, responseType: 'blob' as 'blob'};
    return this.http.get(`${this.url}/atestado/${alunoId}`, requestOptions);
  }


private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      throw(error);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }


(results) => {
			this.searchItems = results;
		}


params => {
			this.currentLanguage=params['language'];
			this.SearchService.getSearch(this.currentLanguage,'').then((results) => {
				this.searchItems = results;
			});
			/*
			this.SearchService.getSearch(this.currentLanguage,'').then((results) => {
				this.searchItems = results;
			});*/
		}


(results) => {
				this.searchItems = results;
			}


@Component({
	selector: 'search-app',
	template: `

        Search
        <input class="form-control" [(ngModel)]="searchText" >

                	 - {{searchItem.category.title}}

`
})

export class SearchComponent {

	// searchResult for templates
	public searchItems: CompanyStructure[];
	public searchString:string;
	private currentLanguage: string;

	set searchText(text: any) {
		this.searchString=text;
		this.SearchService.getSearch(this.currentLanguage,text).then((results) => {
			this.searchItems = results;
		});
	};

	public constructor(private SearchService: CompanyService, private route: ActivatedRoute) {
		this.route.params.subscribe(params => {
			this.currentLanguage=params['language'];
			this.SearchService.getSearch(this.currentLanguage,'').then((results) => {
				this.searchItems = results;
			});
			/*
			this.SearchService.getSearch(this.currentLanguage,'').then((results) => {
				this.searchItems = results;
			});*/
		});
	}
}


/**
 * `Collection of the cached objects`.
 * @private
 */
export declare class PdfCacheCollection {
    /**
     * Stores the similar `objects`.
     * @private
     */
    private referenceObjects;
    /**
     * Stores the references of font with GUID `objects`.
     * @private
     */
    private pdfFontCollection;
    /**
     * Initializes a new instance of the `PdfCacheCollection` class.
     * @private
     */
    constructor();
    /**
     * `Searches` for the similar cached object. If is not found - adds the object to the cache.
     * @private
     */
    search(obj: IPdfCache): IPdfCache;
    /**
     * `Creates` a new group.
     * @private
     */
    createNewGroup(): Object[];
    /**
     * `Find and Return` a group.
     * @private
     */
    getGroup(result: IPdfCache): Object[];
    /**
     * Remove a group from the storage.
     */
    removeGroup(group: Object[]): void;
    destroy(): void;
}


/**
     * `Searches` for the similar cached object. If is not found - adds the object to the cache.
     * @private
     */
search(obj: IPdfCache): IPdfCache;


/**
     * `Creates` a new group.
     * @private
     */
createNewGroup(): Object[];


/**
     * `Find and Return` a group.
     * @private
     */
getGroup(result: IPdfCache): Object[];


/**
     * Remove a group from the storage.
     */
removeGroup(group: Object[]): void;


destroy(): void;


() => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [AddCizimService]
    });
  });

  it('should be created', inject([AddCizimService], (service: AddCizimService) => {
    expect(service).toBeTruthy();
  }));
}


() => {
    TestBed.configureTestingModule({
      providers: [AddCizimService]
    });
  }


(service: AddCizimService) => {
    expect(service).toBeTruthy();
  }


rtMessage => {
      // TODO show ticket number and email
      this.dialogRef.afterClosed()
        .subscribe(() => this.notificator.showSuccess(this.translate.instant('DIALOGS.BUG_REPORT.SUCCESS')));
      this.dialogRef.close();
    }


() => this.notificator.showSuccess(this.translate.instant('DIALOGS.BUG_REPORT.SUCCESS'))


@Component({
  selector: 'app-bug-report-dialog',
  templateUrl: './bug-report-dialog.component.html',
  styleUrls: ['./bug-report-dialog.component.scss']
})
export class BugReportDialogComponent implements OnInit {

  constructor(
    public dialogRef: MatDialogRef<BugReportDialogComponent>,
    private translate: TranslateService,
    private notificator: NotificatorService,
    private rtMessages: RtMessagesService,
    @Inject(MAT_DIALOG_DATA) public data: BugReportData
  ) { }

  message = '';
  subject = '';
  methodRegexp = /(\w+\/\w+)$/g;

  ngOnInit() {
    if (this.data.error && this.data.error.errorId) {
      this.subject = this.translate.instant('DIALOGS.BUG_REPORT.SUBJECT_VALUE') +
        this.parseMethod(this.data.error.call) + ' (' + this.data.error.errorId + ')';
    }
  }

  sendBugReport() {
    this.rtMessages.sendMessageToRT('perun', this.subject, this.getFullEmailBody()).subscribe(rtMessage => {
      // TODO show ticket number and email
      this.dialogRef.afterClosed()
        .subscribe(() => this.notificator.showSuccess(this.translate.instant('DIALOGS.BUG_REPORT.SUCCESS')));
      this.dialogRef.close();
    });
  }

  private parseMethod(url: string): string {
    if (!url) {
      return url;
    }
    return this.methodRegexp.exec(url)[1];
  }

  getFullEmailBody(): string {
    return this.message + '\n' +
      '------------------------\n' +
      'Technical details:\n\n' +
      this.data.error.errorId + ' ' + this.data.error.type + '\n' +
      this.data.error.message + '\n' +

      // TODO add instance
      'Perun instance: GENERIC\n' +
      'Request:\n' +
      this.data.error.call + '\n\n' +
      'Payload:\n' +
      this.data.error.payload + '\n\n' +

      // TODO add version
      'NEW GUI';

  }
}


export interface BugReportData {
  error: RPCError;
}


ngOnInit() {
    if (this.data.error && this.data.error.errorId) {
      this.subject = this.translate.instant('DIALOGS.BUG_REPORT.SUBJECT_VALUE') +
        this.parseMethod(this.data.error.call) + ' (' + this.data.error.errorId + ')';
    }
  }


sendBugReport() {
    this.rtMessages.sendMessageToRT('perun', this.subject, this.getFullEmailBody()).subscribe(rtMessage => {
      // TODO show ticket number and email
      this.dialogRef.afterClosed()
        .subscribe(() => this.notificator.showSuccess(this.translate.instant('DIALOGS.BUG_REPORT.SUCCESS')));
      this.dialogRef.close();
    });
  }


private parseMethod(url: string): string {
    if (!url) {
      return url;
    }
    return this.methodRegexp.exec(url)[1];
  }


getFullEmailBody(): string {
    return this.message + '\n' +
      '------------------------\n' +
      'Technical details:\n\n' +
      this.data.error.errorId + ' ' + this.data.error.type + '\n' +
      this.data.error.message + '\n' +

      // TODO add instance
      'Perun instance: GENERIC\n' +
      'Request:\n' +
      this.data.error.call + '\n\n' +
      'Payload:\n' +
      this.data.error.payload + '\n\n' +

      // TODO add version
      'NEW GUI';

  }


async (data) => {
    const dispatch = useDispatch()
    {
      try {
        const result = client.service('publish-scene').create(data)
        console.log(result)
      } catch (error) {
        console.error(error)
      }
    }
  }


(props: FlexSvgPropsOptional) => (
  <SvgIcon {...props}>
    <Svg viewBox="0 0 24 24">
      <Path d


export function isBinarySerializer(object: any): object is BinarySerializer {
    return 'serializeBinary' in object && typeof object.serializeBinary === "function";
}


export function isBinaryDeserializer(object: any): object is BinaryDeserializer {
    return 'deserializeBinary' in object && typeof object.deserializeBinary === "function";
}


export function XText(url: string, options: XOptions): XRequest {
    let request: XRequest = new XRequest();
    let opts: XOptions = Object.create(options);
    opts.url = url;

    let data = opts.data ? opts.data : undefined;

    if (opts.data !== null && opts.data !== undefined) {
        opts.method = "POST";
    }
    request.open(opts);

    setTextHeaders(request, opts);
    setHeaders(request, opts);
    setFunctionOverrides(request, opts);

    request.send(data);

    return request;
}


export function XJson(url: string, options: XOptions): XRequest {
    let request: XRequest = new XRequest();
    let opts: XOptions = Object.create(options);
    opts.url = url;
    opts.responseType = "json";

    let data = opts.data ? opts.data : undefined;
    if (opts.data !== null && opts.data !== undefined) {
        opts.method = "POST";
    }
    request.open(opts);

    setJSONHeaders(request, opts);
    setHeaders(request, opts);
    setFunctionOverrides(request, opts);

    request.send(data);

    return request;
}