import OSM from 'ol/source/OSM';
import XYZ from 'ol/source/XYZ';
import { Component, createEffect, createSignal, For, Show } from 'solid-js';
import {
  I18nContext,
  createI18nContext,
  useI18n,
} from '@solid-primitives/i18n';

import style from './MapTileProvider.module.css';
import LayersIcon from '@suid/icons-material/Layers';
import CloseIcon from '@suid/icons-material/Close';
import {
  FormControlLabel,
  IconButton,
  Radio,
  RadioGroup,
} from '@suid/material';
import { useNavigate, useParams } from '@solidjs/router';
import Dialog from '../dialog';
import Tree from '../tree';
import { createCachedSignal } from '../../workers/cached-signals';
import dispatch from '../../workers/dispatcher-main';
import getUri from '../../lib/ids';
import { Feature } from 'ol';

import {
  overlayCategories,
  overlayDefinitions,
} from '../overlays/overlay-definitions';
import { cloneDeep } from 'lodash';

const id = getUri('overlays', undefined);

interface TileProvider {
  name: string;
  language: string;
  source: XYZ;
}

type TileProviders = {
  [key: string]: TileProvider;
};

export const mapTileProviders: TileProviders = {
  osm: {
    name: 'Open Street Map',
    language: 'int',
    source: new OSM(),
  },
  osmfr: {
    name: 'Open Street Map France',
    language: 'fr',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 20,
      url: 'https://{a-c}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png',
    }),
  },
  otm: {
    name: 'Open Topo Map',
    language: 'int',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 16,
      url: 'https://{a-c}.tile.opentopomap.org/{z}/{x}/{y}.png',
    }),
  },
  cyclosm: {
    name: 'CyclOSM',
    language: 'int',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 19,
      url: 'https://{a-c}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
    }),
  },
  //https://b.tile.openstreetmap.fr/openriverboatmap/20/535762/382966.png
  openriverboatmap: {
    name: 'Open River Boat Map',
    language: 'int',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 19,
      url: 'https://{a-c}.tile.openstreetmap.fr/openriverboatmap/{z}/{x}/{y}.png',
    }),
  },
  rasterIgnEs: {
    name: 'Cartografía Ráster de España del IGN',
    language: 'es',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 19,
      url: 'http://www.ign.es/wmts/mapa-raster?request=getTile&layer=MTN&TileMatrixSet=GoogleMapsCompatible&TileMatrix={z}&TileCol={x}&TileRow={y}&format=image/jpeg',
    }),
  },
  pnoaMaEs: {
    name: 'Ortoimágenes de España',
    language: 'es',
    source: new XYZ({
      minZoom: 0,
      maxZoom: 19,
      url: 'http://www.ign.es/wmts/pnoa-ma?request=getTile&layer=MTN&TileMatrixSet=GoogleMapsCompatible&TileMatrix={z}&TileCol={x}&TileRow={y}&format=image/jpeg',
    }),
  },
};

type FeatureSubTypes = Array<string> | '*';
type FeatureTypes = Record<string, FeatureSubTypes>;
type Overlay = {
  selected: boolean;
  highlighted: FeatureTypes;
  hidden: FeatureTypes;
};
type Overlays = Record<string, Overlay>;

const defaultOverlayContent = {
  selected: false,
  highlighted: {
    none: [],
  },
  hidden: { none: [] },
};

const defaultOverlays: Overlays = {
  none: { selected: true, highlighted: { none: [] }, hidden: {} },
};

// type OverlayDefinition = Record<string, FeatureTypes>;
// type OverlayDefinitions = Record<string, OverlayDefinition>;

// export const overlayDefinitions: OverlayDefinitions = {
//   none: {},
//   hiking: {
//     none: {},
//     sleeping: {
//       tourism: [
//         'hotel',
//         'alpine_hut',
//         'apartment',
//         'camp_site',
//         'chalet',
//         'guest_house',
//         'hostel',
//         'motel',
//         'wilderness_hut',
//       ],
//     },
//     drinking: {
//       amenity: ['bar', 'cafe', 'pub', 'drinking_water', 'water_point'],
//       natural: ['spring'],
//     },
//     eating: {
//       amenity: ['fast_food', 'pub', 'restaurant'],
//       shop: [
//         'bakery',
//         'butcher',
//         'cheese',
//         'chocolate',
//         'convenience',
//         'dairy',
//         'farm',
//         'greengrocer',
//         'health_food',
//         'pastry',
//         'seafood',
//         'department_store',
//         'supermarket',
//       ],
//     },
//     health: {
//       amenity: ['doctors', 'hospital', 'pharmacy'],
//     },
//     security: {
//       amenity: ['police', 'fire_station'],
//     },
//     dayToday: {
//       amenity: ['waste_basket', 'waste_disposal'],
//       shop: ['laundry'],
//     },
//     naturalSites: {
//       tourism: ['viewpoint'],
//       natural: '*',
//     },
//   },
// };

const getOverlays = createCachedSignal({
  id,
  method: 'getOverlays',
  defaultOverlays,
}) as () => Overlays;

const currentOverlayKey = () =>
  getOverlays()
    ? Object.keys(getOverlays()).filter((key) => getOverlays()[key].selected)[0]
    : 'none';

const currentOverlay = () =>
  getOverlays() ? getOverlays()[currentOverlayKey()] : {};

const overlayCategoriesPlusNone = { none: [], ...overlayCategories };

export const currentCategory = () =>
  currentOverlayKey() === 'none'
    ? []
    : ['none', ...overlayCategoriesPlusNone[currentOverlayKey()]];

const currentOverlayHighlightedKey = () =>
  currentOverlay() && currentOverlay().highlighted
    ? currentOverlay().highlighted
    : 'none';

export const currentOverlayHighlightedDefinition = () =>
  currentCategory()[currentOverlayHighlightedKey()];

export const getAllPoiTypes = () => {
  let result = new Set();
  Object.keys(overlayDefinitions).forEach((tagName) => {
    const tag = overlayDefinitions[tagName];
    result = new Set([...result, ...Object.keys(tag)]);
  });
  return [...result];
};

export const highlightedTags = () => {
  let result = {};
  Object.keys(overlayDefinitions).forEach((tagName) => {
    let tagValues = [];
    const tag = overlayDefinitions[tagName];
    Object.keys(tag).forEach((tagValue) => {
      const catDef = tag[tagValue];
      Object.keys(catDef).forEach((catName) => {
        if (catName === currentOverlayKey()) {
          const catValue = catDef[catName];
          if (catValue[currentOverlay()?.highlighted]) {
            tagValues = [...tagValues, tagValue];
          }
        }
      });
    });
    if (tagValues.length > 0) {
      result[tagName] = tagValues;
    }
  });

  // console.log({
  //   caller: 'MapTileProviders / highlightedTags',
  //   result,
  //   currentOverlayKey: currentOverlayKey(),
  //   currentOverlay: currentOverlay(),
  // });
  return result;
};

// createEffect(() => {
//   highlightedTags = {};
//   Object.keys(overlayDefinitions).forEach((tagName) => {
//     let tagValues = [];
//     const tag = overlayDefinitions[tagName];
//     Object.keys(tag).forEach((tagValue) => {
//       const catDef = tag[tagValue];
//       Object.keys(catDef).forEach((catName) => {
//         if (catName === currentOverlayKey()) {
//           const catValue = catDef[catName];
//           if (catValue[currentOverlay()?.highlighted]) {
//             tagValues = [...tagValues, tagValue];
//           }
//         }
//       });
//     });
//     if (tagValues.length > 0) {
//       highlightedTags[tagName] = tagValues;
//     }
//   });

//   console.log({
//     caller: 'MapTileProviders / createEffect',
//     highlightedTags,
//     currentOverlayKey: currentOverlayKey(),
//     currentOverlay: currentOverlay(),
//   });
// });

export const getHighlightedTagValue = (feature: Feature) => {
  let result = false;

  Object.keys(highlightedTags()).every((tagName) => {
    const value = feature.get(tagName);
    const highlightedValues = highlightedTags()[tagName];
    // console.log({
    //   caller: 'MapTileProviders / highlightedTag / found',
    //   feature,
    //   feature_values: feature.values_,
    //   highlightedTags,
    //   tagName,
    //   tag_value: value,
    //   highlightedValues,
    // });

    if (value !== undefined && highlightedValues.includes(value)) {
      result = value;
      return false;
    }
    return true;
  });
  // console.log({
  //   caller: 'MapTileProviders / highlightedTag / result',
  //   feature,
  //   feature_values: feature.values_,
  //   highlightedTags,
  //   result,
  // });
  return result;
};

export const isHighlighted = (feature: Feature) => {
  return !!getHighlightedTagValue(feature);
};

export const getTagValue = (feature: Feature) => {
  let result = false;
  Object.keys(overlayDefinitions).every((tagName) => {
    const value = feature.get(tagName);
    const tagValues = overlayDefinitions[tagName];

    if (value !== undefined && Object.keys(tagValues).includes(value)) {
      // console.log({
      //   caller: 'MapTileProviders / tagValue / found',
      //   feature,
      //   feature_values: feature.values_,
      //   highlightedTags,
      //   tagName,
      //   tag_value: value,
      //   highlightedValues,
      // });
      result = value;
      return false;
    }
    return true;
  });
  // console.log({
  //   caller: 'MapTileProviders / isHighlighted / false',
  //   feature,
  //   feature_values: feature.values_,
  //   highlightedTags,
  // });
  return result;
};

const MapTilesProvider: Component<{}> = (props) => {
  const [open, setOpen] = createSignal(false);

  const navigate = useNavigate();

  const handleClickOpen = () => {
    setOpen(true);
  };
  const handleClose = () => {
    setOpen(false);
  };

  const params = useParams();

  const [t] = useI18n();

  const handleChange = (ev: any) => {
    navigate(
      `/map/${ev.target.value}/${params.lon}/${params.lat}/${params.zoom}/${params.rotation}`
    );
    setOpen(false);
  };

  createEffect(() => {
    console.log({
      caller: 'MapTilesProvider',
      overlays: getOverlays(),
      currentOverlayKey: currentOverlayKey(),
      currentOverlay: currentOverlay(),
      currentOverlayDefinition: currentCategory(),
      currentOverlayHighlightedKey: currentOverlayHighlightedKey(),
    });
  });

  const getOverlaysAddingSelectedIfMissing = (key: string) => {
    const overlays = getOverlays();
    if (!Object.keys(overlays).includes(key)) {
      overlays[key] = cloneDeep(defaultOverlayContent);
    }
    return overlays;
  };

  const handleOverlayChange = (ev: any) => {
    const value = ev.target.value;
    console.log({
      caller: 'MapTilesProvider / handleOverlayChange',
      ev,
      value,
    });
    const newOverlays = getOverlaysAddingSelectedIfMissing(value);
    Object.keys(newOverlays).forEach((key) => {
      newOverlays[key].selected = key === value;
    });
    dispatch({
      action: 'putOverlays',
      params: { id, overlays: newOverlays },
    });
  };

  const handleOverlayHighlightChange = (ev: any) => {
    const value = ev.target.value;
    console.log({
      caller: 'MapTilesProvider / handleOverlayHighlightChange',
      ev,
      value,
    });
    const newOverlays = getOverlays();
    newOverlays[currentOverlayKey()].highlighted = value;
    dispatch({
      action: 'putOverlays',
      params: { id, overlays: newOverlays },
    });
  };

  return (
    <>
      <div class={style.control}>
        <IconButton onClick={handleClickOpen}>
          <LayersIcon />
        </IconButton>
      </div>
      <Dialog
        closeHandler={handleClose}
        open={open()}
        title={t('chooseYourMap')}
      >
        <Tree
          title={t('overlay')}
          content={
            <>
              <RadioGroup
                defaultValue={currentOverlayKey()}
                onChange={handleOverlayChange}
              >
                <For each={Object.keys(overlayCategoriesPlusNone)}>
                  {(p: string) => (
                    <FormControlLabel value={p} control={<Radio />} label={p} />
                  )}
                </For>
              </RadioGroup>
              <Show when={currentCategory().length > 0}>
                <div>---</div>
                <div>Highlight</div>
                <RadioGroup
                  defaultValue={currentOverlayHighlightedKey()}
                  onChange={handleOverlayHighlightChange}
                >
                  <For each={currentCategory()}>
                    {(p: string) => (
                      <FormControlLabel
                        value={p}
                        control={<Radio />}
                        label={p}
                      />
                    )}
                  </For>
                </RadioGroup>
              </Show>
            </>
          }
          subTree={undefined}
        ></Tree>

        <Tree
          title={t('baseLayer')}
          content={
            <RadioGroup defaultValue={params.provider} onChange={handleChange}>
              <For each={Object.keys(mapTileProviders)}>
                {(p: string) => (
                  <FormControlLabel
                    value={p}
                    control={<Radio />}
                    label={mapTileProviders[p].name}
                  />
                )}
              </For>
            </RadioGroup>
          }
          subTree={undefined}
        ></Tree>
      </Dialog>
    </>
  );
};

export default MapTilesProvider;
