<template>
  <div id="app">
    <div class="notificationContainer">
      <div class="notification" :class="{ error: notification != null && notification.isError }" v-if="notification != null" @click.prevent="hideNotification">
        <span class="message">{{ notification.message }}</span>
      </div>
    </div>

    <div id="container">
      <div class="header">
        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0E2MTY4NzJGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0E2MTY4NzNGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3QTYxNjg3MEZCMUExMUVBOERFMkUzQkYzOTcyNENENSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3QTYxNjg3MUZCMUExMUVBOERFMkUzQkYzOTcyNENENSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgI/6YAAAAfkSURBVHjazFpbbBRV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" />
        <h1>{{ $t('title') }}</h1>
        <h2>{{ $t('systemID') }}{{ status.systemID || '...' }}</h2>

        <div class="wifistatus">
          <div class="connection">
            <div class="indicator" :data-status="wifiStatus.ap.enabled ? 'connected' : 'disconnected'"></div> {{ $t('wifiStatus.accesspoint.title') }} {{ wifiStatus.ap.enabled ? wifiStatus.ap.ip : $t('wifiStatus.accesspoint.disabled') }}
          </div>
          <div class="connection">
            <div class="indicator" :data-status="getWiFiStationStatus()"></div> {{ $t('wifiStatus.stationmode.title') }} {{ getWiFiStationStatusText() }}
          </div>
        </div>
      </div>

      <div v-if="loading" class="loading">
        <LoadingIndicator></LoadingIndicator>
      </div>

      <div v-else>
        <div class="warning" v-if="hasResetError">
          <p>
            {{ $t('error.resetError') }}
          </p>

          <p class="resetReason">
            {{ $t('error.resetReason.' + status.resetReason) }}
          </p>

          <p v-if="status.stackTrace">
            {{ $t('error.stackTrace') }}
          </p>

          <a class="button button-primary" href="/api/stacktrace/get" v-if="status.stackTrace">{{ $t('error.stackTraceDownload') }}</a>
          <a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
        </div>


        <div class="navigation tabs">
          <router-link to="/" class="button" active-class="active" exact="true">{{ $t('status.tabTitle') }}</router-link><router-link to="/connection" class="button" active-class="active">{{ $t('connection.tabTitle') }}</router-link><router-link to="/system" class="button" active-class="active">{{ $t('system.tabTitle') }}</router-link>
        </div>


        <router-view/>


        <div class="clearfix"></div>
      </div>
      <div class="version">
        {{ $t('copyright') }}<br>
        {{ $t('firmwareVersion') }}{{ status.version || '...' }}
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
import LoadingIndicator from '@/components/loadingIndicator.vue';

export default {
  components: {
    LoadingIndicator
  },

  data()
  {
    return {
      loading: true,

      status: {
        systemID: null,
        version: null,
        resetReason: null,
        stackTrace: false
      },

      wifiStatus: {
        ap: {
          enabled: false,
          ip: '0.0.0.0'
        },
        station: {
          enabled: false,
          status: 0,
          ip: '0.0.0.0'
        }
      }
    };
  },


  mounted()
  {
    const self = this;

    self.updateStatus()
      .then(() =>
      {
        self.updateWiFiStatus()
          .then(() =>
          {
            self.loading = false;
          });
      });
  },


  computed: {
    notification() { return this.$store.state.notification; },
    saving() { return this.$store.state.saving; },

    hasResetError()
    {
      /*
        REASON_DEFAULT_RST      = 0  normal startup by power on
        REASON_WDT_RST          = 1  hardware watch dog reset
        REASON_EXCEPTION_RST    = 2  exception reset, GPIO status won’t change
        REASON_SOFT_WDT_RST     = 3  software watch dog reset, GPIO status won’t change
        REASON_SOFT_RESTART     = 4  software restart ,system_restart , GPIO status won’t change
        REASON_DEEP_SLEEP_AWAKE = 5  wake up from deep-sleep
        REASON_EXT_SYS_RST      = 6   system reset
      */
      return (this.status.resetReason === 1 ||
              this.status.resetReason === 2 ||
              this.status.resetReason === 3 ||
              this.status.stackTrace);
    }
  },


  methods: {
    getWiFiStationStatus()
    {
      if (!this.wifiStatus.station.enabled)
        return 'disconnected';

      switch (this.wifiStatus.station.status)
      {
        case 0: // WL_IDLE_STATUS
        case 2: // WL_SCAN_COMPLETED
          return 'connecting';

        case 1: // WL_NO_SSID_AVAIL
        case 4: // WL_CONNECT_FAILED
        case 5: // WL_CONNECTION_LOST
          return 'error';

        case 3: // WL_CONNECTED
          return 'connected';

        case 6: // WL_DISCONNECTED
        default:
          return 'disconnected';
      }
    },


    getWiFiStationStatusText()
    {
      if (!this.wifiStatus.station.enabled)
        return this.$i18n.t('wifiStatus.stationmode.disabled');

      switch (this.wifiStatus.station.status)
      {
        case 0: // WL_IDLE_STATUS
          return this.$i18n.t('wifiStatus.stationmode.idle');

        case 1: // WL_NO_SSID_AVAIL
          return this.$i18n.t('wifiStatus.stationmode.noSSID');

        case 2: // WL_SCAN_COMPLETED
          return this.$i18n.t('wifiStatus.stationmode.scanCompleted');

        case 3: // WL_CONNECTED
          return this.wifiStatus.station.ip;

        case 4: // WL_CONNECT_FAILED
          return this.$i18n.t('wifiStatus.stationmode.connectFailed');

        case 5: // WL_CONNECTION_LOST
          return this.$i18n.t('wifiStatus.stationmode.connectionLost');

        case 6: // WL_DISCONNECTED
        default:
          return this.$i18n.t('wifiStatus.stationmode.disconnected');
      }
    },


    updateStatus()
    {
      var self = this;

      return axios.get('/api/status', { retry: 10, retryDelay: 1000 })
        .then(response =>
        {
          if (typeof response.data == 'object')
            self.status = response.data;
        })
        .catch(e => self.handleAPIError('error.loadStatus', e));
    },


    updateWiFiStatus()
    {
      const self = this;

      return new Promise((resolve, reject) =>
      {
        if (!self.saving)
        {
          axios.get('/api/connection/status', { retry: 10, retryDelay: 1000 })
            .then(response =>
            {
              if (typeof response.data == 'object')
                self.wifiStatus = response.data;
            })
            .catch(e =>
            {
              self.handleAPIError('error.updateWiFiStatus', e);
              reject(e);
            })
            .then(function()
            {
              setTimeout(self.updateWiFiStatus, 5000);
              resolve();
            });
        }
        else
        {
          setTimeout(self.updateWiFiStatus, 5000);
          resolve();
        }
      });
    },


    deleteStackTrace()
    {
      const self = this;

      return axios.get('/api/stacktrace/delete', { retry: 10, retryDelay: 1000 })
        .then(response =>
        {
          self.status.resetReason = 0;
          self.status.stackTrace = false;
        })
        .catch(e => self.handleAPIError('error.stackTraceDeleteError', e));
    },


    handleAPIError(messageId, error)
    {
      this.$store.dispatch('notifyAPIError', { message: this.$i18n.t(messageId), error });
    },


    hideNotification()
    {
      this.$store.dispatch('hideNotification');
    }
  }
}
</script>

<style lang="scss">
@import "variables.scss";


html
{
  overscroll-behavior-x: contain;
  box-sizing: border-box;
  font-size: 62.5%;
}

*, *:before, *:after
{
  box-sizing: inherit;
}

body
{
  overscroll-behavior-x: contain;
  background-color: rgb(20, 20, 20);
  color: white;
  font-family: 'Verdana', 'Arial', sans-serif;
  font-size: 1.3em;
  font-weight: 300;
  letter-spacing: .01em;
  line-height: 1.3;

  padding-bottom: 3rem;

  @media #{$mediumScreen}
  {
    padding-top: 3rem;
  }
}

a
{
  text-decoration: none;
}


#container
{
  background: $containerBackground;
  margin-top: 2rem;
  padding: 1rem;

  box-shadow: 0 0 50px $containerShadowColor;
  border: solid 1px black;

  @media #{$mediumScreen}
  {
    width: 768px;
    margin-left: auto;
    margin-right: auto;
  }
}


.header
{
  position: relative;

  img
  {
    float: left;
    margin-right: 1rem;
  }

  .wifistatus
  {
    @media #{$smallScreen}
    {
      clear: both;
      margin-top: 3rem;
    }

    @media #{$mediumScreen}
    {
      position: absolute;
      right: 0;
      top: 0;
    }

    .indicator
    {
      display: inline-block;
      width: 1rem;
      height: 1rem;
      border-radius: 50%;
      margin-right: 0.5rem;

      &[data-status=connected] { background-color: #339966; }
      &[data-status=disconnected] { border: solid 1px #808080; }
      &[data-status=connecting] { background-color: #ff9933; }
      &[data-status=error] { background-color: #cc0000; }
    }
  }
}


%outset
{
  border: 1px solid #111111;
  border-radius: 3px;
  box-shadow: inset 0 1px rgba(255,255,255,0.1), inset 0 -1px 3px rgba(0,0,0,0.3), inset 0 0 0 1px rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.15);
}


%inset
{
  border: 1px solid #111111;
  border-color: black #111111 #111111;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.25),0 1px rgba(255,255,255,0.08);
}


button, input
{
  font-family: 'Verdana', 'Arial', sans-serif;
}


@mixin removeSafariStyling
{
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}

input
{
  @include removeSafariStyling;
}

button, .button, input[type=submit]
{
  @extend %outset;

  display: inline-block;
  padding: 0 12px;
  color: $buttonTextColor;
  background: $buttonBackground;
  cursor: pointer;
  line-height: 3rem;

  &:hover, &:focus, &.focus
  {
    color: $buttonHoverTextColor;
    background: $buttonHoverBackground;
    outline: none
  }

  &:active, &.active
  {
    @extend %inset;

    color: $buttonActiveTextColor;
    background: $buttonActiveBackground;
  }
}

input[type=submit], .button-primary
{
  background: $buttonPrimaryBackground;

  &:hover, &:focus, &.focus
  {
    background: $buttonPrimaryHoverBackground;
  }
}

a.button
{
  text-decoration: none
}


.navigation
{
  clear: both;
  margin-top: 3rem;
}

.tabs
{
  &>.button
  {
    margin-left: -1px;
    border-radius: 0;

    &:first-child
    {
      margin-left: 0;
      border-radius: 3px 0 0 3px
    }

    &:last-child
    {
      border-radius: 0 3px 3px 0
    }

    &:focus
    {
      position: relative;
      z-index: 1
    }
  }
}


.version
{
  color: $versionTextColor;
  font-size: 8pt;
  text-align: center;
  margin-top: 2rem;
}


.notificationContainer
{
  position: fixed;
  top: 2rem;
  z-index: 666;

  @media #{$mediumScreen}
  {
    width: 512px;
    left: 50%;
  }
}


.notification
{
  @extend %outset;

  background: $notificationBackground;
/*  border: solid 1px $notificationBorderColor;*/
  box-shadow: 0 0 10px black;
  color: white;
  cursor: pointer;
  padding: .5em;
  margin-bottom: 2rem;

  position: relative;

  @media #{$mediumScreen}
  {
    left: -50%;
  }

  .message
  {
    white-space: pre;
  }


  &.error
  {
    background: $notificationErrorBackground;
  }
}


.check, .radio
{
  display: inline-block;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  margin-top: .5em;
  margin-bottom: .5em;

  .control
  {
    @extend %outset;
    background: $checkRadioBackground;
    display: inline-block;
    width: 16px;
    height: 16px;
    position: relative;
  }

  .label
  {
    display: inline-block;
    margin-left: .5em;
    vertical-align: top;
  }

  &.checked
  {
    .control
    {
      background: $checkRadioSelectedBackground;

      .inner
      {
      }
    }
  }

  &.disabled
  {
    cursor: not-allowed;

    .label
    {
      color: $inputDisabledTextColor;
    }
  }
}


.radio
{
  .control, .control .inner
  {
    border-radius: 50%;
  }

  .control .inner
  {
    color: black;
    position: absolute;
    top: 4px;
    left: 4px;
    width: 6px;
    height: 6px;
  }

  &.checked .control .inner
  {
    background: #cccccc;
    box-shadow: 0 1px rgba(0,0,0,0.5);
  }
}


.check
{
  .control .inner
  {
    position: absolute;
    top: 5px;
    left: 4px;
    width: 6px;
    height: 3px;
  }

  &.checked .control .inner
  {
    border: solid rgba(255,255,255,0.8);
    border-width: 0 0 2px 2px;
    transform: rotate(-45deg);
    box-shadow: -1px 0 rgba(0,0,0,0.2), 0 1px rgba(0,0,0,0.5)
  }
}


.form-control
{
  margin-top: 1em;
}


input[type=text], input[type=number], input[type=password], textarea
{
  @extend %inset;
  background: $inputBackground;
  color: $inputTextColor;
  padding: .5em;
  width: 100%;
}

select
{
  @extend %outset;
  background: $selectBackground;
  color: $inputTextColor;
  font-family: 'Verdana', 'Arial', sans-serif;
  padding: .5em;
}

input[type=range]
{
  margin-top: 1rem;
  margin-bottom: 1rem;
}

h1
{
  font-size: 2rem;
  margin: 0;
}

h2
{
  color: #c0c0c0;
  font-size: 1.2rem;
  margin: 0;
}

h3
{
  @extend %outset;
  color: $sectionHeaderTextColor;
  background: $sectionHeaderBackground;
  font-size: 1.2rem;
  padding: .5rem;
}

h4
{
  font-size: 1.4rem;
}


input[disabled]
{
  cursor: not-allowed;
  color: $inputDisabledTextColor;
  background: $inputDisabledBackground;
}

label
{
  display: block;
  margin-top: .5em;
  margin-bottom: .5em;
}



.label-inline
{
  margin-right: 2rem;
}



@media #{$mediumScreen}
{
  .horizontal
  {
    clear: both;

    label
    {
      display: inline-block;
    }

    input[type=text], input[type=number], input[type=password], textarea
    {
      display: inline-block;
      float: right;
      width: 50%;
    }

    &:after
    {
      clear: both;
    }
  }
}

.hint
{
  display: block;
  font-size: 8pt;
  color: #808080;
  margin-bottom: 1.5rem;
}


.loading
{
  margin-top: 3rem;
  text-align: center;
}


.suboptions
{
  margin-left: 5rem;
}


.buttons
{
  clear: both;
  text-align: center;
  margin-top: 1rem;
}


.sliders
{
  margin-top: 2rem;
}


.slidercontainer
{
  margin-top: 1rem;
}



$sliderRedThumbColor: #ce3636;
$sliderGreenThumbColor: #32b732;
$sliderBlueThumbColor: #4646cc;
$sliderWhiteThumbColor: #fcf6cf;


.slider
{
  -webkit-appearance: none;
  width: 100%;
  height: $sliderBarSize;
  border-radius: $sliderBarSize / 2;
  background: $sliderBarColor;
  outline: none;

  &::-webkit-slider-thumb
  {
    -webkit-appearance: none;
    appearance: none;
    width: $sliderThumbSize;
    height: $sliderThumbSize;
    border-radius: 50%;
    background: $sliderThumbColor;
    cursor: pointer;
  }

  &::-moz-range-thumb
  {
    width: $sliderThumbSize;
    height: $sliderThumbSize;
    border-radius: 50%;
    background: $sliderThumbColor;
    cursor: pointer;
  }


  &.red
  {
    &::-webkit-slider-thumb { background: $sliderRedThumbColor; }
    &::-moz-range-thumb { background: $sliderRedThumbColor; }
  }

  &.green
  {
    &::-webkit-slider-thumb { background: $sliderGreenThumbColor; }
    &::-moz-range-thumb { background: $sliderGreenThumbColor; }
  }

  &.blue
  {
    &::-webkit-slider-thumb { background: $sliderBlueThumbColor; }
    &::-moz-range-thumb { background: $sliderBlueThumbColor; }
  }
}


.warning
{
  @extend %outset;
  background: #973a38;
  padding: .5em;
  margin-bottom: 2rem;
  margin-top: 1rem;
}


.nodata
{
  color: #808080;
  text-align: center;
}


.clear
{
  clear: both;
}


.panel
{
  margin-bottom: 2rem;
  padding: 0;

  .panel-header
  {
    @extend %outset;
    border-radius: 3px 3px 0 0;
    border-bottom-width: 0;
    padding: .5em;

    label {
      font-size: 1em;
    }

    background: $panelHeaderBackground;
    color: $panelHeaderTextColor;

    .actions
    {
      float: right;
    }

    a, .label
    {
      color: $panelHeaderLinkColor;
    }
  }

  .panel-body
  {
    @extend %outset;
    border-radius: 0 0 3px 3px;

    background: $panelBodyBackground;
    padding: 2rem;
  }


  &.active
  {
    .panel-header
    {
      background: $panelActiveHeaderBackground;
      color: $panelActiveHeaderTextColor;
    }
  }
}


.inline
{
  display: inline-block;
  width: auto;
}

.fade-enter-active, .fade-leave-active
{
  transition: opacity .5s;
}

.fade-enter, .fade-leave-to
{
  opacity: 0;
}


.range
{
  clear: both;

  .start
  {
    position: relative;
    display: inline-block;
    width: 49%;

    .slidercontainer
    {
      margin-right: 4em;
    }

    .value
    {
      position: absolute;
      right: 0;
      top: 1.5rem;
      color: $sliderValueColor;
    }
  }

  .end
  {
    position: relative;
    display: inline-block;
    float: right;
    width: 50%;

    .slidercontainer
    {
      margin-left: 4em;
    }

    .value
    {
      position: absolute;
      left: 0;
      top: 1.5rem;
      color: $sliderValueColor;
    }
  }

  &:after
  {
    clear: both;
  }
}


.resetReason
{
  margin-left: 2em;
}
</style>
