'use strict';

var LocationPicker = (function () {
  var GEO = null;
  var GEO_LC = null;
  var COUNTRIES = null;
  var POSTAL = null;
  var POSTAL_LC = null;
  var COORDS = null;
  var dataPromise = null, postalPromise = null, countriesPromise = null, coordsPromise = null;

  function url(name) { return chrome.runtime.getURL(name); }
  function leadLC(canonical) {
    var i = canonical.indexOf(',');
    return (i < 0 ? canonical : canonical.slice(0, i)).toLowerCase();
  }

  function loadCountries() {
    if (countriesPromise) return countriesPromise;
    countriesPromise = fetch(url('geotargets-countries.json')).then(function (r) { return r.json(); })
      .then(function (d) { COUNTRIES = d; return d; });
    return countriesPromise;
  }

  function loadData() {
    if (dataPromise) return dataPromise;
    dataPromise = fetch(url('geotargets.json')).then(function (r) { return r.json(); })
      .then(function (d) { GEO = d; GEO_LC = d.map(function (p) { return leadLC(p[0]); }); return d; });
    return dataPromise;
  }

  function loadPostal() {
    if (postalPromise) return postalPromise;
    postalPromise = fetch(url('geotargets-postal.json')).then(function (r) { return r.json(); })
      .then(function (d) { POSTAL = d; POSTAL_LC = d.map(function (p) { return leadLC(p[0]); }); return d; });
    return postalPromise;
  }

  function loadCoords() {
    if (coordsPromise) return coordsPromise;
    coordsPromise = fetch(url('geocoords.json')).then(function (r) { return r.json(); })
      .then(function (d) { COORDS = d; return d; });
    return coordsPromise;
  }

  function placeCoords(p) {
    if (!COORDS || !p) return null;
    return COORDS[leadLC(p[0]) + '|' + (p[1] || '').toLowerCase()] || null;
  }

  function search(q, limit) {
    q = q.trim().toLowerCase();
    if (q.length < 2) return [];
    var placePrefix = [], placeContains = [], countryHits = [], postal = [];

    if (GEO) {
      for (var i = 0; i < GEO_LC.length; i++) {
        if (GEO_LC[i].indexOf(q) === 0) { placePrefix.push(GEO[i]); if (placePrefix.length >= limit) break; }
      }
      for (var j = 0; j < GEO_LC.length && placeContains.length < limit; j++) {
        if (GEO_LC[j].indexOf(q) > 0) placeContains.push(GEO[j]);
      }
    }
    if (COUNTRIES) {
      var cp = [], cc = [];
      for (var m = 0; m < COUNTRIES.length; m++) {
        var name = COUNTRIES[m][1].toLowerCase();
        var pos = name.indexOf(q);
        if (pos === 0) cp.push([COUNTRIES[m][1], COUNTRIES[m][0], 'Country']);
        else if (pos > 0) cc.push([COUNTRIES[m][1], COUNTRIES[m][0], 'Country']);
      }
      countryHits = cp.concat(cc);
    }
    if (/\d/.test(q) && POSTAL_LC) {
      for (var k = 0; k < POSTAL_LC.length && postal.length < limit; k++) {
        if (POSTAL_LC[k].indexOf(q) === 0) postal.push([POSTAL[k][0], POSTAL[k][1], 'Postal Code']);
      }
    }
    return placePrefix.concat(countryHits, placeContains, postal).slice(0, limit);
  }
  function isCountry(p) { return p && p[2] === 'Country'; }

  function pretty(canonical) { return canonical.split(',').join(', '); }
  function placeCanonical(p) { return p[0]; }
  function placeGl(p) { return (p[1] || 'us').toLowerCase(); }

  function el(tag, cls, html) {
    var e = document.createElement(tag);
    if (cls) e.className = cls;
    if (html != null) e.innerHTML = html;
    return e;
  }

  function mount(container, onChange) {
    onChange = onChange || function () {};
    var state = { place: null, useCoords: false, lat: '', lng: '' };
    var postalLoaded = false;

    container.classList.add('loc');
    container.innerHTML = '';

    var input = el('input', 'loc-input');
    input.type = 'text';
    input.placeholder = 'City, area, postcode or country — e.g. Pune, 110008, London, India…';
    input.setAttribute('autocomplete', 'off');
    input.spellcheck = false;

    var hint = el('p', 'loc-hint', 'Pick a city/area/postcode for a precise check, or a country for a country-level check.');

    var suggest = el('ul', 'loc-suggest'); suggest.hidden = true;

    var chip = el('div', 'loc-chip'); chip.hidden = true;
    var chipText = el('span', 'loc-chip-text');
    var chipX = el('button', 'loc-chip-x', '&times;'); chipX.type = 'button'; chipX.title = 'Clear location';
    chip.appendChild(chipText); chip.appendChild(chipX);

    var advToggle = el('button', 'loc-adv-toggle', 'Advanced: exact coordinates (pin) ▾');
    advToggle.type = 'button';
    var adv = el('div', 'loc-adv'); adv.hidden = true;
    var coordRow = el('div', 'loc-coord-row');
    var latWrap = el('label', 'loc-coord', 'Latitude');
    var latIn = el('input', 'loc-lat'); latIn.type = 'number'; latIn.step = 'any'; latIn.placeholder = '28.6448';
    latWrap.appendChild(latIn);
    var lngWrap = el('label', 'loc-coord', 'Longitude');
    var lngIn = el('input', 'loc-lng'); lngIn.type = 'number'; lngIn.step = 'any'; lngIn.placeholder = '77.1728';
    lngWrap.appendChild(lngIn);
    coordRow.appendChild(latWrap); coordRow.appendChild(lngWrap);
    var useWrap = el('label', 'loc-usecoords');
    var useCb = el('input'); useCb.type = 'checkbox';
    useWrap.appendChild(useCb); useWrap.appendChild(document.createTextNode(' Use these exact coordinates'));
    var note = el('p', 'loc-adv-note',
      'A named place above is the most reliable target. Use coordinates for a spot Google doesn’t list ' +
      'by name (e.g. Shadipur): right-click it in Google Maps to copy its lat/long, paste here, and tick the box.');
    adv.appendChild(coordRow); adv.appendChild(useWrap); adv.appendChild(note);

    var globalWrap = el('label', 'loc-global');
    var globalCb = el('input'); globalCb.type = 'checkbox'; globalCb.className = 'loc-switch-input';
    var globalTrack = el('span', 'loc-switch-track');
    var globalKnob = el('span', 'loc-switch-knob', '<svg class="ic" aria-hidden="true"><use href="#i-pin"></use></svg>');
    globalTrack.appendChild(globalKnob);
    var globalTxt = el('span', 'loc-global-text',
      'Use this location for <strong>all</strong> my Google searches');
    globalWrap.appendChild(globalCb); globalWrap.appendChild(globalTrack); globalWrap.appendChild(globalTxt);
    var globalNote = el('p', 'loc-global-note'); globalNote.hidden = true;

    container.appendChild(input);
    container.appendChild(hint);
    container.appendChild(suggest);
    container.appendChild(chip);
    container.appendChild(advToggle);
    container.appendChild(adv);
    container.appendChild(globalWrap);
    container.appendChild(globalNote);

    function globalActiveText(label) {
      return 'ON — Google searches in this browser now show results for <strong>' + label +
        '</strong>, even outside this tool and after this window closes. Untick to go back to your real location.';
    }
    function reflectGlobal(on, label) {
      globalWrap.classList.toggle('is-on', !!on);
      if (on) { globalNote.innerHTML = globalActiveText(label || ''); globalNote.hidden = false; }
      else globalNote.hidden = true;
    }

    function syncGlobal() {
      if (!globalCb.checked) {
        chrome.storage.local.set({ globalLoc: { on: false } });
        reflectGlobal(false);
        return;
      }
      var v = value();
      chrome.storage.local.set({ globalLoc: { on: true, mode: v.mode, gl: v.gl, uule: v.uule, xgeo: v.xgeo, label: v.label } });
      reflectGlobal(true, v.label);
    }

    function maybeSyncGlobal() { if (globalCb.checked) syncGlobal(); }

    globalCb.addEventListener('change', function () {
      if (globalCb.checked && !state.place && !(state.useCoords && state.lat && state.lng)) {
        globalCb.checked = false;
        globalNote.innerHTML = 'Pick a location above first, then turn this on.';
        globalNote.hidden = false;
        return;
      }
      syncGlobal();
    });

    chrome.storage.local.get('globalLoc').then(function (d) {
      var g = d.globalLoc;
      if (g && g.on) { globalCb.checked = true; reflectGlobal(true, g.label); }
    });
    chrome.storage.onChanged.addListener(function (changes, area) {
      if (area !== 'local' || !changes.globalLoc) return;
      var g = changes.globalLoc.newValue || { on: false };
      globalCb.checked = !!g.on;
      reflectGlobal(g.on, g.label);
    });

    function reflectSelection() {
      if (state.place) {
        chip.hidden = false;
        chipText.textContent = pretty(state.place[0]) + (state.place[2] ? ' · ' + state.place[2] : '');
        input.hidden = true;
        hint.hidden = true;
      } else {
        chip.hidden = true;
        input.hidden = false;
        hint.hidden = false;
      }
      onChange(value());
    }

    function renderSuggest(list) {
      suggest.innerHTML = '';
      if (!list.length) { suggest.hidden = true; return; }
      list.forEach(function (p) {
        var li = el('li', 'loc-opt');
        li.appendChild(el('span', 'loc-opt-name', pretty(p[0])));
        if (p[2]) li.appendChild(el('span', 'loc-opt-type', p[2]));
        li.addEventListener('mousedown', function (ev) { ev.preventDefault(); selectPlace(p); });
        suggest.appendChild(li);
      });
      suggest.hidden = false;
    }

    function selectPlace(p) {
      state.place = p;
      input.value = '';
      suggest.hidden = true;

      if (isCountry(p)) {
        onChange(value());
        maybeSyncGlobal();
      } else {
        loadCoords().then(function () {
          var ll = placeCoords(state.place);
          if (ll) {
            if (!latIn.value) latIn.value = ll[0];
            if (!lngIn.value) lngIn.value = ll[1];
          }
          onChange(value());
          maybeSyncGlobal();
        });
      }
      reflectSelection();
    }

    function clearPlace() {
      state.place = null;
      input.value = '';
      input.focus();

      if (globalCb.checked) { globalCb.checked = false; syncGlobal(); }
      reflectSelection();
    }

    function refresh() { renderSuggest(search(input.value, 8)); }

    function loadSearchData() { return Promise.all([loadData(), loadCountries()]); }

    input.addEventListener('input', function () {
      var v = input.value;
      loadSearchData().then(function () {
        refresh();
        if (/\d/.test(v) && !postalLoaded) { postalLoaded = true; loadPostal().then(refresh); }
      });
    });
    input.addEventListener('focus', function () {
      if (input.value.trim().length >= 2) loadSearchData().then(refresh);
    });
    input.addEventListener('blur', function () { setTimeout(function () { suggest.hidden = true; }, 120); });
    input.addEventListener('keydown', function (e) { if (e.key === 'Escape') suggest.hidden = true; });
    chipX.addEventListener('click', clearPlace);

    advToggle.addEventListener('click', function () {
      adv.hidden = !adv.hidden;
      advToggle.innerHTML = 'Advanced: exact coordinates (pin) ' + (adv.hidden ? '▾' : '▴');
    });
    function onCoordEdit() { state.lat = latIn.value; state.lng = lngIn.value; onChange(value()); maybeSyncGlobal(); }
    latIn.addEventListener('input', onCoordEdit);
    lngIn.addEventListener('input', onCoordEdit);
    useCb.addEventListener('change', function () { state.useCoords = useCb.checked; onChange(value()); maybeSyncGlobal(); });

    function value() {
      var latN = parseFloat(state.lat), lngN = parseFloat(state.lng);

      var coordsOk = state.useCoords && isFinite(latN) && isFinite(lngN) && !isCountry(state.place);
      var gl = state.place ? placeGl(state.place) : 'us';

      if (coordsOk) {
        var pinLabel = state.place
          ? pretty(state.place[0]) + ' · pin ' + latN.toFixed(4) + ',' + lngN.toFixed(4)
          : 'Pin ' + latN.toFixed(4) + ',' + lngN.toFixed(4);
        return { mode: 'coords', gl: gl, uule: UULE.coords(latN, lngN), xgeo: UULE.xgeo(latN, lngN),
          label: pinLabel, lat: latN, lng: lngN };
      }
      if (isCountry(state.place)) {

        return { mode: 'country', gl: gl, uule: '', xgeo: '', label: state.place[0] + ' (country-level)' };
      }
      if (state.place) {
        var ll = placeCoords(state.place);
        return {
          mode: 'place', gl: gl, uule: UULE.named(placeCanonical(state.place)),
          xgeo: ll ? UULE.xgeo(ll[0], ll[1]) : '',
          label: pretty(state.place[0]) + (state.place[2] ? ' · ' + state.place[2] : ''),
          lat: ll ? ll[0] : null, lng: ll ? ll[1] : null
        };
      }

      return { mode: 'country', gl: 'us', uule: '', xgeo: '', label: 'United States (country-level)' };
    }

    function set(saved) {
      if (!saved) return;
      loadCountries().then(function () {
        if (saved.place) {
          state.place = saved.place;
          if (!isCountry(saved.place)) loadCoords().then(function () { onChange(value()); });
        }
        if (saved.lat != null && saved.lat !== '') { latIn.value = saved.lat; state.lat = String(saved.lat); }
        if (saved.lng != null && saved.lng !== '') { lngIn.value = saved.lng; state.lng = String(saved.lng); }
        if (saved.useCoords) { useCb.checked = true; state.useCoords = true; adv.hidden = false; advToggle.innerHTML = 'Advanced: exact coordinates (pin) ▴'; }
        reflectSelection();
      });
    }

    function uiState() {
      return { place: state.place, useCoords: state.useCoords, lat: state.lat, lng: state.lng };
    }

    function clear() {
      if (globalCb.checked) return false;
      state.place = null;
      state.useCoords = false; state.lat = ''; state.lng = '';
      latIn.value = ''; lngIn.value = ''; useCb.checked = false;
      input.value = '';
      suggest.hidden = true;
      adv.hidden = true;
      advToggle.innerHTML = 'Advanced: exact coordinates (pin) ▾';
      globalNote.hidden = true;
      reflectSelection();
      return true;
    }

    loadCountries().then(function () { onChange(value()); });

    return { value: value, set: set, uiState: uiState, clear: clear, focus: function () { input.focus(); } };
  }

  return { mount: mount, preload: loadData };
})();
