#include <pebble.h>
#include "main.h"

// Largest expected inbox and outbox message sizes
const uint32_t inbox_size = 64;
const uint32_t outbox_size = 64;


static char s_auth[40];
static char s_api[40];
static char s_username[40];
static char s_password[40];
static char s_server[100];
static bool s_js_ready;

static Window *s_window;

static TextLayer *s_text_layer,*s_statusbar;

static ActionBarLayer *s_action_bar_layer;
static StatusBarLayer *s_status_bar_layer;

static GBitmap *s_h_on_bitmap, *s_h_off_bitmap, *s_q_mark_bitmap;
static const int16_t MARGIN = 8;

bool is_home_on = false;

static uint32_t size ;
  
time_t current_time; 


static char * msg;

// An instance of the struct
Settings settings;

// Save the settings to persistent storage
static void prv_save_settings() {
  persist_write_data(SETTINGS_KEY, &settings, sizeof(settings));
}

//! helper to construct the various text layers as they appear in this app
static GRect init_text_layer(Layer *parent_layer, TextLayer **text_layer, int16_t y, int16_t h, int16_t additional_right_margin, char *font_key) {
  // why "-1" (and then "+2")? because for this font we need to compensate for weird white-spacing
  const int16_t font_compensator = strcmp(font_key, FONT_KEY_LECO_38_BOLD_NUMBERS) == 0 ? 3 : 1;

  const GRect frame = GRect(MARGIN - font_compensator, y, layer_get_bounds(parent_layer).size.w - 2 * MARGIN + 2 * font_compensator - additional_right_margin, h);

  *text_layer = text_layer_create(frame);
  text_layer_set_background_color(*text_layer, GColorClear);
  text_layer_set_text_color(*text_layer, PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack));
  text_layer_set_font(*text_layer, fonts_get_system_font(font_key));
  layer_add_child(parent_layer, text_layer_get_layer(*text_layer));
  return frame;
}

static void init_statusbar_text_layer(Layer *parent, TextLayer **layer) {
  init_text_layer(parent, layer, 0, 16, 0, FONT_KEY_GOTHIC_18_BOLD);
  GRect sb_bounds = layer_get_bounds(text_layer_get_layer(*layer));
  sb_bounds.origin.y -= 1;
  layer_set_bounds(text_layer_get_layer(*layer), sb_bounds);
  text_layer_set_text_alignment(*layer, GTextAlignmentCenter);
}

static void inbox_dropped_callback(AppMessageResult reason, void *context) {
  // A message was received, but had to be dropped
  APP_LOG(APP_LOG_LEVEL_ERROR, "Message dropped. Reason: %d", (int)reason);
}

static void outbox_sent_callback(DictionaryIterator *iter, void *context) {
  // The message just sent has been successfully delivered
  APP_LOG(APP_LOG_LEVEL_INFO, "Message sent. ");
}

static void outbox_failed_callback(DictionaryIterator *iter, AppMessageResult reason, void *context) {
  // The message just sent failed to be delivered
  APP_LOG(APP_LOG_LEVEL_ERROR, "Message send failed. Reason: %d", (int)reason);
}


static void select_click_handler(ClickRecognizerRef recognizer, void *context) {

  // Declare the dictionary's iterator
  DictionaryIterator *out_iter;
  
  // Prepare the outbox buffer for this message
  AppMessageResult result = app_message_outbox_begin(&out_iter);
  
  if(result == APP_MSG_OK) {
    // Add an item to ask for weather data
    msg = "get";
    dict_write_cstring(out_iter, status, msg);
  
    // Send this message
    result = app_message_outbox_send();
    if(result != APP_MSG_OK) {
      APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
    }else{
      text_layer_set_text(s_text_layer, "Sent...");
    }
  } else {
    // The outbox cannot be used right now
    APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
  }
  
  
}

static void up_click_handler(ClickRecognizerRef recognizer, void *context) {

  // Declare the dictionary's iterator
  DictionaryIterator *out_iter;
  
  // Prepare the outbox buffer for this message
  AppMessageResult result = app_message_outbox_begin(&out_iter);
  
  if(result == APP_MSG_OK) {
    // Add an item to switch home mode on
    msg = "home_on";
    dict_write_cstring(out_iter, status, msg);
  
    // Send this message
    result = app_message_outbox_send();
    if(result != APP_MSG_OK) {
      APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
    }else{
      text_layer_set_text(s_text_layer, "Sent...");
    }
  } else {
    // The outbox cannot be used right now
    APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
  }
  
  
}

static void down_click_handler(ClickRecognizerRef recognizer, void *context) {

  // Declare the dictionary's iterator
  DictionaryIterator *out_iter;
  
  // Prepare the outbox buffer for this message
  AppMessageResult result = app_message_outbox_begin(&out_iter);
  
  if(result == APP_MSG_OK) {
    // Add an item to switch home mode off
    msg = "home_off";
    dict_write_cstring(out_iter, status, msg);
  
    // Send this message
    result = app_message_outbox_send();
    if(result != APP_MSG_OK) {
      APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
    }else{
      APP_LOG(APP_LOG_LEVEL_INFO, "Sending to the outbox: %s", msg);
      text_layer_set_text(s_text_layer, "Sent...");
    }
  } else {
    // The outbox cannot be used right now
    APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
  }
  
  
}

static void click_config_provider(void *context) {
  window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
  window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
  window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
}

static void update_time() {
  // Get a tm structure
  time_t temp = time(NULL);
  struct tm *tick_time = localtime(&temp);

  // Write the current hours and minutes into a buffer
  static char s_buffer[8];
  strftime(s_buffer, sizeof(s_buffer), clock_is_24h_style() ?
                                          "%H:%M" : "%I:%M", tick_time);

  // Display this time on the TextLayer
  text_layer_set_text(s_statusbar, s_buffer);
  
}

static void comm_is_ready() {

  // Authenticate to get token from syno

  // Declare the dictionary's iterator
  DictionaryIterator *out_iter;
  
  // Prepare the outbox buffer for this message
  AppMessageResult result = app_message_outbox_begin(&out_iter);
  
  if(result == APP_MSG_OK) {
    // Add an item to ask for weather data
    msg = "auth";
    dict_write_cstring(out_iter, status, msg);
  
    // Send this message
    result = app_message_outbox_send();
    if(result != APP_MSG_OK) {
      APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
    }else{
      text_layer_set_text(s_text_layer, "Authenticating...");
    }
  } else {
    // The outbox cannot be used right now
    APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
  }

  // set the text
  //text_layer_set_text(s_text_layer, "Welcome to Syno Cam Switch ! ready");

  // click provider
  window_set_click_config_provider(s_window, click_config_provider);

}

static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
  update_time();  
}

// Handle the response from AppMessage
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
  // A new message has been successfully received
  APP_LOG(APP_LOG_LEVEL_DEBUG, "New message! ");
  
  size = dict_size(iter);
  
  // JS readiness 
  Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_JSReady);
  
  if(ready_tuple){
    //Pebblekit JS is ready ! Safe to send messages
    s_js_ready = true;
    comm_is_ready();
    APP_LOG(APP_LOG_LEVEL_DEBUG, "received jsready message on watch... ");
  }

  // Read Auth returns
  Tuple *auth_tuple = dict_find(iter, MESSAGE_KEY_auth);

  if(auth_tuple) {
    strncpy(s_auth, auth_tuple->value->cstring, 40);
    // Display in the TextLayer
    text_layer_set_text(s_text_layer, s_auth);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "Auth message received ... ");
  }else{
    APP_LOG(APP_LOG_LEVEL_DEBUG, "not auth message... ");
  }

  // Read API returns
  Tuple *api_tuple = dict_find(iter, MESSAGE_KEY_status);

  if(api_tuple) {
    strncpy(s_api, api_tuple->value->cstring, 40);
    // Display in the TextLayer
    text_layer_set_text(s_text_layer, s_api);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "status message received ... ");
  }else{
    APP_LOG(APP_LOG_LEVEL_DEBUG, "not status message... ");
  }
 
  // Read String preferences
  Tuple *username_t = dict_find(iter, MESSAGE_KEY_username);
  if(username_t) {
    strncpy(s_username, username_t->value->cstring, 40);
    prv_save_settings();
    APP_LOG(APP_LOG_LEVEL_DEBUG, "new username in settings... %s",s_username);
  }

  Tuple *password_t = dict_find(iter, MESSAGE_KEY_password);
  if(password_t) {
    strncpy(s_password, password_t->value->cstring, 40);
    prv_save_settings();
    APP_LOG(APP_LOG_LEVEL_DEBUG, "new password in settings... %s",s_password);
  }
  
  Tuple *server_t = dict_find(iter, MESSAGE_KEY_server);
  if(server_t) {
    strncpy(s_server, server_t->value->cstring, 99);
    prv_save_settings();
    APP_LOG(APP_LOG_LEVEL_DEBUG, "new server in settings... %s",s_server);
  }

}

static void init(void) {

  // Open AppMessage
  app_message_open(inbox_size, outbox_size);
  
  // Register to be notified about inbox received events
  app_message_register_inbox_received(inbox_received_callback);
  // Register to be notified about inbox dropped events
  app_message_register_inbox_dropped(inbox_dropped_callback);
  // Register to be notified about outbox sent events
  app_message_register_outbox_sent(outbox_sent_callback);
  // Register to be notified about outbox failed events
  app_message_register_outbox_failed(outbox_failed_callback);
  
  // Create a window and get information about the window
  s_window = window_create();
  Layer *window_layer = window_get_root_layer(s_window);
  GRect bounds = layer_get_bounds(window_layer);
  
  //load buttons
  s_h_on_bitmap = gbitmap_create_with_resource(RESOURCE_ID_HOME_ON);
  s_q_mark_bitmap = gbitmap_create_with_resource(RESOURCE_ID_Q_MARK);
  s_h_off_bitmap = gbitmap_create_with_resource(RESOURCE_ID_HOME_OFF);

  s_action_bar_layer = action_bar_layer_create();
  action_bar_layer_set_icon(s_action_bar_layer, BUTTON_ID_UP, s_h_on_bitmap);
  action_bar_layer_set_icon(s_action_bar_layer, BUTTON_ID_SELECT, s_q_mark_bitmap);
  action_bar_layer_set_icon(s_action_bar_layer, BUTTON_ID_DOWN, s_h_off_bitmap);
  action_bar_layer_add_to_window(s_action_bar_layer, s_window);
  
  s_status_bar_layer = status_bar_layer_create();

  Layer *status_bar_base_layer = status_bar_layer_get_layer(s_status_bar_layer);
  GRect status_frame = layer_get_frame(status_bar_base_layer);
  status_frame.origin.y = -STATUS_BAR_LAYER_HEIGHT;
  layer_set_frame(status_bar_base_layer, status_frame);

  layer_add_child(window_layer, status_bar_base_layer);

  if (bounds.origin.y == 0) {
    bounds.origin.y = STATUS_BAR_LAYER_HEIGHT;
    bounds.size.h -= STATUS_BAR_LAYER_HEIGHT;

    GRect frame = layer_get_frame(window_layer);
    frame.size.h = bounds.size.h;

    layer_set_frame(window_layer, frame);
    layer_set_bounds(window_layer, bounds);
    layer_set_clips(window_layer, false);
  }

  init_statusbar_text_layer(window_layer, &s_statusbar);
  text_layer_set_text(s_statusbar, "");


  // Create a text layer and set the text
  const GEdgeInsets label_insets = {.right = ACTION_BAR_WIDTH, .left = ACTION_BAR_WIDTH / 2};
  s_text_layer = text_layer_create(grect_inset(bounds, label_insets));
  text_layer_set_text(s_text_layer, "Welcome to Syno Cam Switch !");
  
  // Set the font and text alignment
  text_layer_set_font(s_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
  text_layer_set_text_alignment(s_text_layer, GTextAlignmentCenter);

  // Add the text layer to the window
  layer_add_child(window_layer, text_layer_get_layer(s_text_layer));
  
  // Enable text flow and paging on the text layer, with a slight inset of 5, for round screens
  text_layer_enable_screen_text_flow_and_paging(s_text_layer, 5);

  // Push the window, setting the window animation to 'true'
  window_stack_push(s_window, true);
  
  // get the current time !
  //update_time();
  //Kick the tick_handler for instant update
  current_time = time(NULL);
  tick_handler(localtime(&current_time),MINUTE_UNIT);
  tick_timer_service_subscribe((MINUTE_UNIT), tick_handler);

  // App Logging!
  APP_LOG(APP_LOG_LEVEL_DEBUG, "Just pushed a window!");
  
}

static void deinit(void) {
  // Destroy the text layer
  text_layer_destroy(s_text_layer);

  // Destroy the action bar layer
  action_bar_layer_destroy(s_action_bar_layer);

  // Destroy the status bar layer
  status_bar_layer_destroy(s_status_bar_layer);
  
  // Destroy the window
  window_destroy(s_window);
}

int main(void) {
  init();
  app_event_loop();
  deinit();
}
