/*
 * This file is part of the "bluetoothheater" distribution 
 * (https://gitlab.com/mrjones.id.au/bluetoothheater) 
 *
 * Copyright (C) 2018  Ray Jones <ray@mrjones.id.au>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 * 
 */

#include "ScreenManager.h"
#include "DetailedScreen.h"
#include "BasicScreen.h"
#include "PrimingScreen.h"
#include "WiFiScreen.h"
#include "FuelMixtureScreen.h"
#include "SetClockScreen.h"
#include "SetTimerScreen.h"
#include "ClockScreen.h"
#include "RebootScreen.h"
#include "HeaterSettingsScreen.h"
#include "SettingsScreen.h"
#include "ThermostatModeScreen.h"
#include "FontDumpScreen.h"
#include "TimerChartScreen.h"
#include "InheritSettingsScreen.h"
#include "GPIOScreen.h"
#include "VersionInfoScreen.h"
#include "HomeMenuSelScreen.h"
#include "OtherOptionsScreen.h"
#include <Wire.h>
#include "../cfg/pins.h"
#include "../cfg/BTCConfig.h"
#include "../protocol/helpers.h"
#include "keypad.h"
#include "fonts/Icons.h"
#include "fonts/MiniFont.h"


////////////////////////////////////////////////////////////////////////////////////////////////
// splash creen created using image2cpp http://javl.github.io/image2cpp/
//   Settings: 
//      Black background
//      Invert [X]
//      Arduino code, single bitmap
//      Identifier: DieselSplash
//      Draw Mode: Horizontal
//
const unsigned char DieselSplash [] PROGMEM = {
// 'Splash3, 128x64px
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x18, 0x00, 0x00, 0x00, 0x00, 
	0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 
	0x02, 0x3e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 
	0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 
	0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
	0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
	0x00, 0x08, 0x00, 0x00, 0x01, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x88, 0x00, 0x00, 
	0x00, 0x10, 0x00, 0x00, 0x20, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x84, 0x00, 0x00, 
	0x00, 0x18, 0x00, 0x00, 0x20, 0x40, 0x00, 0x20, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x84, 0x00, 0x00, 
	0x00, 0x14, 0x00, 0x00, 0x40, 0x40, 0x00, 0x10, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x84, 0x00, 0x00, 
	0x00, 0x52, 0x00, 0x00, 0x40, 0x20, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 
	0x00, 0x34, 0x00, 0x00, 0x40, 0x10, 0x00, 0x04, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x18, 0x00, 0x00, 0x80, 0x10, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x34, 0x00, 0x00, 0x80, 0x08, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x52, 0x00, 0x00, 0x80, 0x08, 0x01, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x14, 0x00, 0x01, 0x00, 0x04, 0x3e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x18, 0x00, 0x01, 0x00, 0x07, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 
	0x00, 0x10, 0x00, 0x06, 0x80, 0x1c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0xc4, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x18, 0x40, 0x64, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x3c, 0x40, 0x84, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0xc0, 0x40, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x26, 0x23, 0x04, 0x00, 0x00, 0x00, 0x60, 0x00, 0x1e, 0x00, 0x40, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x41, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x43, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x41, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x40, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x60, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x30, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x10, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x19, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x0f, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x07, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xf8, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x1f, 0x20, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x03, 0xe8, 0x20, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x7c, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x01, 0x46, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 
	0x00, 0x02, 0x28, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 
	0x00, 0x02, 0x28, 0x73, 0x2d, 0xc9, 0x5a, 0x8c, 0xb0, 0x20, 0x31, 0xdd, 0x66, 0x53, 0x2c, 0x00, 
	0x00, 0x03, 0xee, 0x44, 0xb1, 0x29, 0x63, 0x52, 0xc0, 0x20, 0x4a, 0x51, 0x89, 0x54, 0xb0, 0x00, 
	0x00, 0x02, 0x28, 0x47, 0xa1, 0x29, 0x42, 0x5e, 0x80, 0x20, 0x4a, 0x51, 0x09, 0x57, 0xa0, 0x00, 
	0x00, 0x02, 0x28, 0x44, 0x21, 0x2b, 0x42, 0x50, 0x80, 0x21, 0x4a, 0x51, 0x09, 0x54, 0x20, 0x00, 
	0x00, 0x02, 0x28, 0x33, 0x21, 0xc5, 0x42, 0x4c, 0x80, 0x1e, 0x32, 0x4d, 0x06, 0x53, 0x20, 0x00
};


CScreenManager::CScreenManager() 
{
  _pDisplay = NULL;
  _menu = -1;
  _subMenu = 0;
  _rootMenu = -1;
  _bReqUpdate = false;
  _DimTime_ms = millis() + 60000;
  _MenuTimeout = millis() + 60000;
  _pRebootScreen = NULL;
  _bDimmed = false;
}

CScreenManager::~CScreenManager()
{
	for(int i=0; i < _Screens.size(); i++) {
  	for(int j=0; j < _Screens[i].size(); j++) {
  		if(_Screens[i][j]) {
			  delete _Screens[i][j];
			  _Screens[i][j] = NULL;
		  }
    }
  }
  if(_pDisplay) {
    delete _pDisplay; _pDisplay = NULL;
  }
}

void 
CScreenManager::begin(bool bNoClock)
{

  // 128 x 64 OLED support (I2C)
  // xxxx_SWITCHCAPVCC = generate display voltage from 3.3V internally
  _pDisplay = new C128x64_OLED(OLED_SDA_pin, OLED_SCL_pin);
#if USE_ADAFRUIT_SH1106 == 1
  _pDisplay->begin(SH1106_SWITCHCAPVCC);
   Wire.begin(OLED_SDA_pin, OLED_SCL_pin, 800000);   // speed up I2C from the default crappy 100kHz set via the adafruit begin!
#elif USE_ADAFRUIT_SSD1306 == 1
  _pDisplay->begin(SSD1306_SWITCHCAPVCC, 0x3c);
  _pDisplay->ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
  _pDisplay->ssd1306_command(0x1F);  // correct lame reversal of OLED current phases
#endif

  // replace adafruit splash screen
  _pDisplay->clearDisplay();
  _pDisplay->drawBitmap(0, 0, DieselSplash, 128, 64, WHITE);
  _pDisplay->setCursor(90, 50);
  CTransientFont AF(*_pDisplay, &miniFontInfo);  // temporarily use a mini font
  _pDisplay->setTextColor(WHITE);
  _pDisplay->print(getVersionStr());

  // Show initial display buffer contents on the screen --
  _pDisplay->display();

  DebugPort.println("Creating Screens");

  std::vector<CScreen*> menuloop;
  // create root menu loop
  menuloop.push_back(new CDetailedScreen(*_pDisplay, *this));         //  detail control
  menuloop.push_back(new CBasicScreen(*_pDisplay, *this));            //  basic control
  if(!bNoClock)
    menuloop.push_back(new CClockScreen(*_pDisplay, *this));          //  clock
  menuloop.push_back(new CPrimingScreen(*_pDisplay, *this));          //  mode / priming
  menuloop.push_back(new CWiFiScreen(*_pDisplay, *this));             //  comms info
  menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this));         //  GPIO info
  menuloop.push_back(new CSettingsScreen(*_pDisplay, *this));         //  Tuning info
  _Screens.push_back(menuloop);

  // create timer screens loop
  menuloop.clear();
  menuloop.push_back(new CTimerChartScreen(*_pDisplay, *this, 0)); // timer chart
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 0)); // set timer 1
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 1)); // set timer 2
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 2)); // set timer 3
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 3)); // set timer 4
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 4)); // set timer 5
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 5)); // set timer 6
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 6)); // set timer 7
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 7)); // set timer 8
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 8)); // set timer 9
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 9)); // set timer 10
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 10)); // set timer 11
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 11)); // set timer 12
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 12)); // set timer 13
  menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 13)); // set timer 14
  _Screens.push_back(menuloop);

  // create heater tuning screens loop - password protected
  menuloop.clear();
  menuloop.push_back(new CFuelMixtureScreen(*_pDisplay, *this));      //  tuning
  menuloop.push_back(new CHeaterSettingsScreen(*_pDisplay, *this));   // tuning
  _Screens.push_back(menuloop);

  // create User Settings screens loop 
  menuloop.clear();
  menuloop.push_back(new CThermostatModeScreen(*_pDisplay, *this)); // experimental settings screen
  menuloop.push_back(new CGPIOScreen(*_pDisplay, *this)); // GPIO settings screen
  menuloop.push_back(new CVersionInfoScreen(*_pDisplay, *this)); // GPIO settings screen
  menuloop.push_back(new CHomeMenuSelScreen(*_pDisplay, *this)); // Home menu settings screen
  menuloop.push_back(new COtherOptionsScreen(*_pDisplay, *this)); // Other options screen
  _Screens.push_back(menuloop);

  // create branch screens
  menuloop.clear();
  menuloop.push_back(new CSetClockScreen(*_pDisplay, *this));         // clock set branch screen
  menuloop.push_back(new CInheritSettingsScreen(*_pDisplay, *this));  // inherit OEM settings branch screen
  menuloop.push_back(new CFontDumpScreen(*_pDisplay, *this)); // font dump branch screen
  _Screens.push_back(menuloop);

  _menu = 0;
#if RTC_USE_DS3231==0 && RTC_USE_DS1307==0 && RTC_USE_PCF8523==0
  _rootMenu = 2;   // bring up clock set screen first if using millis based RTC!
  _subMenu = 2;
#else
	_rootMenu = 1;   // basic control screen
  _subMenu = 1;
#endif

  _enterScreen();
}

bool 
CScreenManager::checkUpdate()
{
  long dimTimeout = NVstore.getDimTime();

  // manage dimming or blanking the display, according to user defined inactivity interval
  if(dimTimeout && _DimTime_ms) {
    long tDelta = millis() - _DimTime_ms;
    if(tDelta > 0) {
      // time to dim the display
      _dim(true);
      _DimTime_ms = 0;

      if(dimTimeout < 0) {
        _pDisplay->clearDisplay();
        _pDisplay->display();   // blank screen
      }
    }
  }

  if(NVstore.getMenuTimeout() && _MenuTimeout) {
    long tDelta = millis() - _MenuTimeout;
    if(tDelta > 0) {
      _MenuTimeout = 0;
      // we will be blanking the display, transit through a dim stage first
      if(dimTimeout < 0)
        _dim(true);

      _leaveScreen();
      // fall back to main menu 
      selectMenu(RootMenuLoop);
      // upon dim timeout - sticky root menu screens are the first 3 in the list:
      //   Detailed Control
      //   Basic Control
      //   Clock
      // return to those upon timeout, otherwise return to Basic Control screen
      if(_rootMenu > 2) {
        uint8_t userHomeMenu = NVstore.getHomeMenu().onTimeout;
        if(userHomeMenu) {  // allow user to override defualt screen
          userHomeMenu--;
          DebugPort.print("Screen Manager: Menu timeout, falling back to user preferred screen: "); 
          switch(userHomeMenu) {
            case 0: DebugPort.println("Detailed control menu"); break;
            case 1: DebugPort.println("Basic control menu"); break;
            case 2: DebugPort.println("Clock menu"); break;
          }
          _rootMenu = _subMenu = userHomeMenu;  
        }
        else {
          _rootMenu = _subMenu = 1;
          DebugPort.print("Screen Manager: Menu timeout, falling back to Basic control screen"); 
        }
      }
      _enterScreen();
    }
  }  
  
  static int prevRunState = -1;
  int runState = getHeaterInfo().getRunStateEx(); 
  if(runState != prevRunState) {
    if(runState > 0 && prevRunState == 0) {
      // heater has started
      uint8_t userStartMenu = NVstore.getHomeMenu().onStart;
      if(userStartMenu && userStartMenu <= 3) {  // allow user to override defualt screen
        userStartMenu--;
        DebugPort.print("Screen Manager: Heater start detected, switching to user preferred screen: "); 
        switch(userStartMenu) {
          case 0: DebugPort.println("Detailed control menu"); break;
          case 1: DebugPort.println("Basic control menu"); break;
          case 2: DebugPort.println("Clock menu"); break;
        }
        _rootMenu = _subMenu = userStartMenu;  
        _enterScreen();
      }
    }
    if(runState == 0 && prevRunState != 0) {
      // heater has stopped
      uint8_t userStopMenu = NVstore.getHomeMenu().onStop;
      if(userStopMenu && userStopMenu <= 3) {  // allow user to override defualt screen
        userStopMenu--;
        DebugPort.print("Screen Manager: Heater stop detected, switching to user preferred screen: "); 
        switch(userStopMenu) {
          case 0: DebugPort.println("Detailed control menu"); break;
          case 1: DebugPort.println("Basic control menu"); break;
          case 2: DebugPort.println("Clock menu"); break;
        }
        _rootMenu = _subMenu = userStopMenu;  
        _enterScreen();
      }
    }
    prevRunState = runState;
  }


  if(_bReqUpdate) {
    if((dimTimeout < 0) && (_DimTime_ms == 0)) {
      // no screen updates, we should be blanked!
    }
    else {
      if(_pRebootScreen) {
        _pRebootScreen->show();
        _bReqUpdate = false;
        return true;
      }
      else {
        if(_menu >= 0) {
          _Screens[_menu][_subMenu]->show();
          _bReqUpdate = false;
          return true;
        }
      }
    }
  }
  return false;
}
  
void 
CScreenManager::reqUpdate()
{
  _bReqUpdate = true;
}

bool 
CScreenManager::animate()
{
  if((NVstore.getDimTime() < 0) && (_DimTime_ms == 0)) {
    // no screen updates, we should be blanked!
    return false;
  }
  if(_menu >= 0) 
    return _Screens[_menu][_subMenu]->animate();
	return false;
}

void
CScreenManager::refresh()
{
	if(_pDisplay)
		_pDisplay->display();
}

void 
CScreenManager::_enterScreen()
{
  if(_menu >= 0) 
    _Screens[_menu][_subMenu]->onSelect();
		
  reqUpdate();
}

void
CScreenManager::_leaveScreen()
{
  if(_menu >= 0) 
    _Screens[_menu][_subMenu]->onExit();
}

void
CScreenManager::_changeSubMenu(int dir)
{
  _leaveScreen();
  _subMenu += dir;
  int bounds = _Screens[_menu].size() - 1;
  ROLLUPPERLIMIT(_subMenu, bounds, 0);
  ROLLLOWERLIMIT(_subMenu, 0, bounds);
  if(_menu == 0)
    _rootMenu = _subMenu;  // track the root menu for when we branch then return
  _enterScreen();
}

void 
CScreenManager::nextMenu()
{
  if(_menu >= 0 && _menu != BranchMenu) {
    _changeSubMenu(+1);
  }
}

void 
CScreenManager::prevMenu()
{
  if(_menu >= 0 && _menu != BranchMenu) {
    _changeSubMenu(-1);
  }
}

void 
CScreenManager::keyHandler(uint8_t event)
{
  long dimTime = NVstore.getDimTime();

  if(_bDimmed) {
    if(event & keyReleased) {
      _dim(false);
      _DimTime_ms = (millis() + abs(dimTime)) | 1;
      _MenuTimeout = millis() + NVstore.getMenuTimeout();
    }
    return;   // initial press when dimmed is always thrown away
  }

//  _dim(false);
  _DimTime_ms = (millis() + abs(dimTime)) | 1;
  _MenuTimeout = millis() + NVstore.getMenuTimeout();

  // call key handler for active screen
  if(_menu >= 0) 
    _Screens[_menu][_subMenu]->keyHandler(event);
}

void
CScreenManager::selectMenu(eUIMenuSets menuSet, int specific)
{
  _leaveScreen();
  if(_menu >= 0) {   // only true once we have created the screens
    _menu = menuSet;
    if(specific >= 0) {
      // targetting a specific menu
      _subMenu = specific;
      UPPERLIMIT(_subMenu, _Screens[_menu].size()-1);  // check bounds!
    }
    else {
      // default sub menu behaviour
      if(_menu == 0)
        _subMenu = _rootMenu; // return to last used root menu
      else
        _subMenu = 0;  // branches always go to first sub menu
    }
  }
  _enterScreen();
}

void 
CScreenManager::showRebootMsg(const char* content[2], long delayTime)
{
  if(_pRebootScreen == NULL)
    _pRebootScreen = new CRebootScreen(*_pDisplay, *this);

  _pRebootScreen->setMessage(content, delayTime);
  _bReqUpdate = true;
  _dim(false);
}

void 
CScreenManager::showOTAMessage(int percent)
{
  static int prevPercent = -1;
  if(percent != prevPercent) {
    _pDisplay->clearDisplay();
    _pDisplay->setCursor(64,22);
    _pDisplay->printCentreJustified("OTA update active");
    if(percent) {
      char msg[16];
      sprintf(msg, "%d%%", percent);
      _pDisplay->setCursor(64,32);
      _pDisplay->printCentreJustified(msg);
    }
    prevPercent = percent;
    _pDisplay->display();
  }
}

void
CScreenManager::_dim(bool state)
{
  _bDimmed = state;
  _pDisplay->dim(state);
}