$(window).on('load', () => {
  window.context = {};
  window.context.books = [];

  var synth = window.speechSynthesis;

  var inputForm = document.querySelector('form');
  var inputTxt = document.querySelector('.txt');
  var voiceSelect = document.querySelector('select');

  var pitch = document.querySelector('#pitch');
  var pitchValue = document.querySelector('.pitch-value');
  var rate = document.querySelector('#rate');
  var rateValue = document.querySelector('.rate-value');

  var voices = [];


  context.nosleep_timer = null;

  context.ui = {
    voice_settings_div: $('.voice-settings'),
    voice_select: $('.voice-select'),
    status_pre: $('.status'),
    books_select: $('.screen .widget select[name=book]'),
    current_sentence_input:
      $('.screen .widget input[name=current-sentence]'),
    total_sentences_input:
      $('.screen .widget input[name=total-sentences]'),
    read_aloud:
      $('.screen .widget input[name=read-aloud]'),
    debug:
      $('.screen .widget input[name=debug]'),
  };
  context.sentences = null;
  context.pending_stop = false;
  context.current_book = null;
  context.nosleep = new NoSleep();
  context.is_debug = false;
  context.log = {
    error: [],
    info: [],
  };
  context.callbacks = {
    log_error: (msg) => {
      if (context.is_debug)
      {
        console.error(msg);
        context.log.error.push(msg);
      }
    },
    enable_no_sleep: () => {
      if (context.nosleep_timer != null)
      {
        context.callbacks.log_error('running already');
      }

      context.nosleep_timer = setInterval(
        () => {
          location.hash = 'nosleep' + Math.random();
          context.callbacks.update_status();
          /*
          if ('vibrate' in window.navigator)
          {
            window.navigator.vibrate(200);
          }
          */
        }, 1000
      );
    },
    get_state: () => {
      let t1 = localStorage['state'];
      if (t1)
      {
        return JSON.parse(t1);
      }
      else
      {
        return {};
      }
    },
    get_cookie: (key) => {
      /*
      return document.cookie.split('; ').map(
        (o) => o.split('=')
      ).reduce(
        (b, a) => {
          if (a.length == 2) {b[a[0]] = a[1]};
          return b
        },
        {}
      )[key];
      */
      let t1 = localStorage['state'];
      if (t1 != undefined)
      {
        let t2 = JSON.parse(t1);
        return t2[key];
      }
      else
      {
        return undefined;
      }
    },
    set_cookie: (key, value) => {
      let state = context.callbacks.get_state('state');

      state[key] = value;

      //document.cookie = `${key}=${value};`;
      localStorage['state'] = JSON.stringify(state);

      context.callbacks.update_status();
    },
    disable_no_sleep: () => {
      if (context.nosleep_timer == null)
      {
        context.callbacks.log_error('nothing is running');
      }
      clearInterval(context.nosleep_timer);
      location.hash = '';
      context.nosleep_timer = null;
      synth.cancel();
    },
    continuous_reading: async() => {
      if (context.is_reading)
      {
        context.pending_stop = true;
        return;
      }
      context.is_reading = true;
      context.nosleep.enable();
      context.callbacks.enable_no_sleep();
      context.ui.voice_settings_div.addClass('hidden');
      context.ui.current_sentence_input.attr(
        'disabled',
        'disabled'
      );

      while (
        context.callbacks.get_cookie('sentence_id') < context.sentences.length &&
        !context.pending_stop 
      )
      {
        let sentence =
          context.sentences[context.callbacks.get_cookie('sentence_id')];
        //context.callbacks.log_error('start');
        try {
          await context.read_aloud(
            context.sentences[
              context.callbacks.get_cookie('sentence_id')
            ]
          );
        } catch (e) {
          context.callbacks.log_error(e);
        }
        //context.callbacks.log_error('finished');
        if (!context.pending_stop)
        {
          context.callbacks.set_cookie(
            'sentence_id',
            context.callbacks.get_cookie('sentence_id') + 1
          );
        }
      }
      context.pending_stop = false;
      context.ui.current_sentence_input.removeAttr('disabled');
      context.nosleep.disable();
      context.ui.voice_settings_div.removeClass('hidden');
      context.callbacks.disable_no_sleep();
      context.is_reading = false;
    },
    update_status: () => {
      let data = {};
      data.state = context.callbacks.get_state();
      if (
        context.callbacks.get_cookie('sentence_id') != null &&
        context.sentences != null &&
        context.callbacks.get_cookie('sentence_id') < context.sentences.length
      )
      {
        data.sentence = context.sentences[context.callbacks.get_cookie('sentence_id')];
      }
      data.pending_stop = context.pending_stop;
      data.is_reading = context.is_reading;
      data.log = context.log;
      context.ui.current_sentence_input.val(
        context.callbacks.get_cookie('sentence_id')
      );
      data.timestamp = (new Date());
      data.version = 'v0.1.7';
      data.speech_synthesis = {
        paused: synth.paused,
        pending: synth.pending,
        speaking: synth.speaking,
      };
      /*
      if (!synth.speaking && context.is_reading)
      {
        synth.cancel();
      }
      */
      context.ui.status_pre.text(
        JSON.stringify(
          data,
          null,
          4,
        )
      );
    },
    ui_read_aloud_on_click: async() => {
      let book_id = parseInt(context.ui.books_select.val());
      if (context.current_book != book_id)
      {
        context.current_book = book_id;
        context.sentences =
          context.books[
            context.current_book
          ].replaceAll(/([\.\?\!])\s+/g,'$1\n')
          .split('\n');
        context.ui.total_sentences_input.val(
          context.sentences.length,
        );
        {
          let state = context.callbacks.get_state();
        }
      }
      if (
        context.ui.current_sentence_input.val() != ''
      )
      {
        try{
          let sentence_id = parseInt(
            context.ui.current_sentence_input.val()
          );

          if (
            sentence_id >= 0 &&
            sentence_id < context.sentences.length
          )
          {
            context.callbacks.set_cookie(
              'sentence_id',
              sentence_id
            );
          }
        } catch (e) {
          context.callbacks.log_error(e);
        }
      }
      if (context.is_reading && !context.pending_stop)
      {
        context.pending_stop = true;
      }
      else
      {
        context.callbacks.continuous_reading();
      }
    },
    populateVoiceList: () => {
      voices = synth.getVoices().sort(function (a, b) {
          const aname = a.name.toUpperCase(), bname = b.name.toUpperCase();
          if ( aname < bname ) return -1;
          else if ( aname == bname ) return 0;
          else return +1;
      });
      //var selectedIndex = voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
      voiceSelect.innerHTML = '';
      for(i = 0; i < voices.length ; i++) {
        var option = document.createElement('option');
        option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
        
        if(voices[i].default) {
          option.textContent += ' -- DEFAULT';
        }


        {
          let voice = context.callbacks.get_cookie('voice');
          if (voice && option.textContent == voice)
          {
            $(option).attr('selected', 'selected');
          }
        }

        option.setAttribute('data-lang', voices[i].lang);
        option.setAttribute('data-name', voices[i].name);
        voiceSelect.appendChild(option);
      }

      //voiceSelect.selectedIndex = selectedIndex;
    },
    init: () => {
      let state = context.callbacks.get_state();
      context.ui.voice_select.val(state.voice);
      if (!state.book_id)
      {
        context.callbacks.set_cookie(
          'book_id',
          0,
        );
      }
      if (!state.sentence_id)
      {
        context.callbacks.set_cookie(
          'sentence_id',
          0,
        );
      }
      if (state.book_id)
      {
        context.ui.books_select.find(
          '>option',
        ).eq(state.book_id).attr('selected', 'selected');
      }
      if (state.sentence_id)
      {
        context.ui.current_sentence_input.val(
          state.sentence_id,
        );
      }
    },
  };
  context.callbacks.populateVoiceList();
  if (speechSynthesis.onvoiceschanged !== undefined) {
    speechSynthesis.onvoiceschanged = context.callbacks.populateVoiceList;
  }

  context.callbacks.init();

  context.ui.read_aloud.on(
    'click',
    context.callbacks.ui_read_aloud_on_click,
  );
  context.ui.voice_select.on(
    'change',
    () => {
      context.callbacks.set_cookie(
        'voice',
        context.ui.voice_select.val()
      );
    }
  );
  context.ui.debug.on(
    'click',
    () => {
      if (context.is_debug)
      {
        context.is_debug = false;
      }
      else
      {
        context.is_debug = true;
      }
      context.callbacks.update_status();
    }
  );
  context.read_aloud = async (raw_line) => {
    line = raw_line.trim();
    if (line.length == 0)
    {
      return;
    }
    let sleep_detect = null;
    let exit = () => {
      if (sleep_detect != null)
      {
        clearInterval(sleep_detect);
      }
    }
    return new Promise((response, reject) => {
      if (synth.speaking) {
          context.callbacks.log_error('speechSynthesis.speaking');
          if (reject != undefined)
          {
            reject('error');
          }
          return;
      }
      let utterThis = new SpeechSynthesisUtterance(line);
      utterThis.onend = function (event) {
          exit();
          context.callbacks.log_error(
            'SpeechSynthesisUtterance.onend ' + event.error
          );
          if (response != undefined)
          {
            response('done ' + event.error);
          }
      }
      utterThis.onpause = function (event) {
          exit();
          context.callbacks.log_error('SpeechSynthesisUtterance.onpause');
          if (reject != undefined)
          {
            reject('paused ' + event.error);
          }
      }
      utterThis.onerror = function (event) {
          exit();
          context.callbacks.log_error(
            'SpeechSynthesisUtterance.onerror ' + event.error
          );
          if (reject != undefined)
          {
            reject('error ' + event.error);
          }
      }
      let selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
      for(i = 0; i < voices.length ; i++) {
        if(voices[i].name === selectedOption) {
          utterThis.voice = voices[i];
          break;
        }
      }
      //window.alert('fuck3');
      utterThis.pitch = pitch.value;
      utterThis.rate = rate.value;
      synth.speak(utterThis);
      let silence_count = 0;
      sleep_detect = setInterval(
        () => {
          if (!synth.speaking)
          {
            context.callbacks.log_error(
              'silence count is ' + silence_count
            )

            ++silence_count;
          }

          if (silence_count == 3 || context.pending_stop)
          {
            exit();
            if (context.pending_stop)
            {
              synth.cancel();
              reject('pending stop');
            }
            else
            {
              context.callbacks.log_error('phone is sleeping, retry');
              response('utterance is not present');
            }
            /*
            context.read_aloud(
              line
            ).then(response).catch(reject);
            */
          }
        },
        100,
      );
    });
  }

  function speak(){
    let line = inputTxt.value;
    if (line !== '') {
      context.read_aloud(line);
    }
  }

  inputForm.onsubmit = function(event) {
    event.preventDefault();

    speak();

    inputTxt.blur();
  }

  pitch.onchange = function() {
    pitchValue.textContent = pitch.value;
  }

  rate.onchange = function() {
    rateValue.textContent = rate.value;
  }

  voiceSelect.onchange = function(){
    speak();
  }
});