#include "led_strip.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <Arduino.h>

#include <FS.h>
#include <LittleFS.h>
#include "common/led_animation.h"
#include "common/LEDStrip.h"
#include "global.h"
#include "common/neo_colors.h"
#include "common/HSVTable.h"
#include "JsonConstrain.h"
#include "my_board.h"
#include "luma_master.h"
#include "common/luma-stiks.h"

#define STRIP1_PIN RGBLED1_Pin
#define STRIP2_PIN RGBLED2_Pin

#define POWERUP_TIME 3000

static const char* tag = "led_strip";

ANIMATION_PROPS animProps;

LEDSTRIP *strip1;
LEDSTRIP *strip2;
TaskHandle_t Strip1_Task_Handle;
TaskHandle_t Strip2_Task_Handle;
TaskHandle_t FrontLight_Task_Handle;

QueueHandle_t eventQueue = xQueueCreate( 4, sizeof( ANIMATION_EVENT ) );
QueueHandle_t whiteQueue = xQueueCreate( 4, sizeof( ANIMATION_EVENT ) );

ANIMATION_EVENT lastNormalEventMsg;
ANIMATION_EVENT eventMsg;

LUMA_PACKET TempLumaPacket;

int CurrentRunningEventIndex = -1;


void Init_LED_Devices(BOARD_PINS boardPins)
{
  ESP_LOGD(tag, "Strips Initialization entered...");
  File file = LittleFS.open("/cfg/led-devices.json");

  if(!file){
    ESP_LOGE(tag,"Error opening led-devices.json!");
    file.close();
  }else{
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, file);
    file.close();
    if(error){ ESP_LOGE(tag, "led-devices.json deserialize error!.."); return;}

    // ********** STRIP1 ********************************
    JsonObject stripJson = doc["strip1"];
    if(jsonConstrainBool(tag, stripJson, "en", false)){
      int port = jsonConstrain<int>(tag, stripJson, "i2s-ch", 0, 1, 0);  
      int size = jsonConstrain<int>(tag, stripJson, "size", 1, 200, 10);
      //int pin = jsonConstrainInt(stripJson, "pin", 0, 48, RGBLED1_Pin);
      LED_ORDER order= getRGBOrder( jsonConstrainString(tag, stripJson, "rgb-order", "grb").c_str() );
      int shift = jsonConstrain<int>(tag, stripJson, "shift", -size, size, 0);
      int offset = jsonConstrain<int>(tag, stripJson, "offset", -size, size, 0);
      int core = jsonConstrain<int>(tag, stripJson, "core", 0, 1, 0);    
      int powerDiv = jsonConstrain<int>(tag, stripJson, "power-div", 0, 3, 0);  

      strip1 = new LEDSTRIP(port, size, boardPins.rgb1, order, shift, offset);
      strip1->setPowerDiv(powerDiv);
      ESP_LOGI(tag,"Strip1 initialized.");
      ESP_LOGD(tag,"  Size: %d, port: %d, shift: %d, offset: %d", size, port, shift, offset);
      ESP_LOGD(tag,"  Strip1 Task Creating...Core: %d", core);
      xTaskCreatePinnedToCore(Strip1_Control_Task, "LED_Strip1_Task", 1024*10, NULL, 1, &Strip1_Task_Handle, core);
    }

    // ********** STRIP2 ********************************
    stripJson.clear();
    stripJson = doc["strip2"];
    if(jsonConstrainBool(tag, stripJson, "en", false)){
      int port = jsonConstrain<int>(tag, stripJson, "i2s-ch", 0, 1, 0);
      int size = jsonConstrain<int>(tag, stripJson, "size", 1, 200, 10);
      //int pin = jsonConstrainInt(stripJson, "pin", 0, 48, RGBLED2_Pin);
      LED_ORDER order= getRGBOrder( jsonConstrainString(tag, stripJson, "rgb-order", "grb").c_str() );
      int shift = jsonConstrain<int>(tag, stripJson, "shift", -size, size, 0);
      int offset = jsonConstrain<int>(tag, stripJson, "offset", -size, size, 0);
      int core2 = jsonConstrain<int>(tag, stripJson, "core", 0, 1, 0);       
      int powerDiv = jsonConstrain<int>(tag, stripJson, "power-div", 0, 3, 0);  

      strip2 = new LEDSTRIP(port, size, boardPins.rgb2, order, shift, offset);
      strip2->setPowerDiv(powerDiv);
      ESP_LOGI(tag,"Strip2 initialized.");
      ESP_LOGD(tag,"  Size: %d, port: %d, shift: %d, offset: %d", size, port, shift, offset);
      ESP_LOGD(tag,"Strip2 Task Creating...Core: %d", core2);
      xTaskCreatePinnedToCore(Strip2_Control_Task, "LED_Strip2_Task", 1024*8, NULL, 1, &Strip2_Task_Handle, core2);   
    }else{
      ESP_LOGE(tag,"Strip2 disabled");
    }

    JsonObject flightJson = doc["front-light"];
    animProps.frontLight.enabled = jsonConstrainBool(tag, flightJson, "en", false);
    if(animProps.frontLight.enabled){
      animProps.frontLight.relayIndex = jsonConstrain<int>(tag, flightJson, "relay", 0, 3, 0);        
      animProps.frontLight.core = jsonConstrain<int>(tag, flightJson, "core", 0, 1, 1);
      animProps.frontLight.style = (FRONT_LIGHT_STYLE)(flightJson, "style", 0, 1, 0);
      ESP_LOGI(tag, "Front Light initialized..");
      Init_FrontLight();
    }

    JsonObject rlightJson = doc["rear-light"];
    animProps.rearLight.enabled = jsonConstrainBool(tag, rlightJson, "en", false);
    if(animProps.rearLight.enabled){
      animProps.rearLight.relayIndex = jsonConstrain<int>(tag, rlightJson, "relay", 0, 3, 1);    
      animProps.rearLight.buttonIndex = jsonConstrain<int>(tag, rlightJson, "button", 0, 2, 2);    
      animProps.rearLight.rampTime = jsonConstrain<int>(tag, rlightJson, "ramp", 1000, 20000, 3000);   
      animProps.rearLight.rampSteps = jsonConstrain<int>(tag, rlightJson, "steps", 2, 20, 8);   
      animProps.rearLight.min = jsonConstrain<float>(tag, rlightJson, "min", 0.0f, 50.0f, 0.0f); 
      animProps.rearLight.max = jsonConstrain<float>(tag, rlightJson, "max", animProps.rearLight.min, 100.0f, 100.0f); 
      ESP_LOGD(tag, "relay: %d, btn: %d, ramp: %d, steps: %d, min: %.2f, max: %.2f", animProps.rearLight.relayIndex, animProps.rearLight.buttonIndex, animProps.rearLight.rampTime, animProps.rearLight.rampSteps, animProps.rearLight.min, animProps.rearLight.max);

      Initialize_Rear_Control(animProps.rearLight.relayIndex, animProps.rearLight.buttonIndex, animProps.rearLight.rampTime, animProps.rearLight.rampSteps , animProps.rearLight.min, animProps.rearLight.max);
      ESP_LOGI(tag, "Rear Light initialized..");
    }
 
  }
 
}

void Init_FrontLight(void)
{
  xTaskCreatePinnedToCore(FrontLight_Task, "FrontLight_Task", 5000, NULL, 1, &FrontLight_Task_Handle, animProps.frontLight.core);
}

void Init_RearLight(void)
{
  //if(jsonOb.isNull()){ Serial.println(" rear_light json is Null.."); return; }
}

void Test_Animations(void){
  ANIMATION_EVENT event;

  strip1->fill({0,0,0}, 0, strip1->effSize);
  strip1->show(true);
  vTaskDelay(100);

  event.hue = RGBtoHUE(col_blue);
  event.hueRange = 180;
  event.param1 = 11;
  event.speed = 70;

  LEDStrip_FireInit(*strip1);

  while(1){
    event.hue = 0;
    event.hueRange = 359;
    event.param1 = 22;
    event.speed = 70;
    //Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
    event.param1 = 30;
    //Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
    event.param1 = 40;
    //Animation_Sectors(*strip1, event, true, DT_BOTH, true, 6, 2);
    event.param1 = 50;
    Animation_Sectors(*strip1, event, DT_BOTH, 2);

    event.param1 = 50;
    Animation_Dashes(*strip1, event, DT_BOTH, 3);

    event.param1 = 50;   
    Animation_Dashes(*strip1, event, DT_BOTH, 3);
    /*
    Animation_Fire(*strip1, event, RED_FIRE, 5000);
    Animation_Fire(*strip1, event, GREEN_FIRE, 5000);
    Animation_Fire(*strip1, event, BLUE_FIRE, 5000);

    event.col1 = col_blue;
    event.col2 = {255,136,0};
    event.density = 11;
    event.speed = 100;
    Animation_Snakes(*strip1, event, false, DT_FWD, 2, 1);

    event.col1 = HUEtoRGB(359);
    event.col2 = HUEtoRGB(0);
    event.density = 22;
    event.speed = 70;
    Animation_Snakes(*strip1, event, true, DT_BOTH, 12, 4);

    event.col1 = HUEtoRGB(359);
    event.col2 = HUEtoRGB(0);
    event.density = 56;
    event.speed = 50;
    Animation_Snakes(*strip1, event, true, DT_BOTH, 12, 4);
    */
  }
}

void Strip1_Control_Task(void *parameters)
{
  vTaskDelay(1000); // small start delay
  ESP_LOGD(tag, "Strip1 Task Entered....");
  //Test_Animations();

  LEDStrip_FireInit(*strip1); // must be initialized before calling fire animation
  
  // Set default event 1 animation base on profile values
  ANIMATION_EVENT ev; // empty event
  ev.animIndex = 0;
  ev.countDown = 6000;
  ev.param2 = 75;
  ev.type = EV_NON_INJECT;
  PostNewEvent(ev); // Start Animation (White Fill)

  animProps.event[1].type = EV_NORMAL;
  PostNewEvent(animProps.event[1]); // default attraction animation

  while(true){
    xQueueReceive( eventQueue, &eventMsg, portMAX_DELAY );
    ESP_LOGD(tag, "Queue waiting: %d", uxQueueMessagesWaiting(eventQueue));

    if(eventMsg.type == EV_NORMAL ) { // EV_NORMAL
      ESP_LOGD(tag, "Running event: type: EV_NORMAL, event: %d, anim: %d", eventMsg.selfIndex, eventMsg.animIndex);
      lastNormalEventMsg = eventMsg; //save last Normal EventMsg
      if( eventMsg.selfIndex >= 0 ) { 
        CurrentRunningEventIndex = eventMsg.selfIndex; 
      }

      if(Luma_PeerCount() > 0){
        TempLumaPacket.type = LPT_ANIM;
        memcpy(&TempLumaPacket.data, &eventMsg, sizeof(ANIMATION_EVENT));
        Luma_Master_Broadcast(TempLumaPacket);
      }

      if( eventMsg.selfIndex == 0 ){
        RunWhitefillAnimation( eventMsg );    
      }else{ 
        RunAnimation( eventMsg ); 
      }
    }
    else if( eventMsg.type == EV_INJECT ){ // insert and return to previous animation
      ESP_LOGD(tag, "EV_INJECT");
      xQueueSend( eventQueue, &lastNormalEventMsg, 100 ); // requeue previous normal event
      RunPopUpAnimations( eventMsg ); 
    }
    else if( eventMsg.type == EV_NON_INJECT ){
      ESP_LOGD(tag, "EV_NON_INJECT");
      RunPopUpAnimations( eventMsg ); 
    }
    else if( eventMsg.type == EV_TEST ){
      ESP_LOGD(tag, "EV_TEST");
      animStatus.EventTestCountdown = EVENT_TESTMODE_TIMEOUT; // start timeout countdown
      // Should not re-post last event from here in case repeated tests are started.
      // that would cause a backlog of the same previous event.
      if( eventMsg.selfIndex == 0 ){
        RunWhitefillAnimation( eventMsg);    
      }else{ 
        RunAnimation( eventMsg );
      }
    }
  }
}

void PostLastNormalEvent(){
  xQueueSend( eventQueue, &lastNormalEventMsg, 100 ); // requeue previous normal event
  ESP_LOGD(tag, "RePosted Last Normal Event Index: %d", lastNormalEventMsg.selfIndex);
}

// Used to cycle to the next event primarity when usinging buttons
int IncrementEventIndex(){
  int x = (CurrentRunningEventIndex + 1) % animStatus.EventsCount;
  ESP_LOGD(tag, "Increment Event Index from %d to %d", CurrentRunningEventIndex, x);

  animProps.event[x].type = EV_NORMAL;
  PostNewEvent(animProps.event[x]);
  return animStatus.EventsIndex;
}

//EVENT_MSG newEvent;
void PostNewEvent( ANIMATION_EVENT &animEvent) {
  //newEvent.type = type;
  //newEvent.event = animEvent;
  xQueueSend(eventQueue, &animEvent, 100); // queue new event

 // if(type == EV_NORMAL){
    // If whitefill index ( could be restarted )
   // if(animEvent.animIndex == ANIM_WHITEFILL_INDEX){
      //if( countDown == 0 ) { animStatus.countDown = 1000; } // TODO Countdown calc maybe wrong...double check
      //if( animStatus.busy ) { animStatus.repeat = true; }
    //  if( FrontLight_Task_Handle ) { xTaskNotifyGive( FrontLight_Task_Handle ); }
   // }
 // }

  if( Strip1_Task_Handle ){ xTaskNotifyGive( Strip1_Task_Handle ); }
}

void Strip2_Control_Task(void *parameters)
{
  vTaskDelay(100);
  ESP_LOGD(tag, "Strip2 Task Entered....\n");

  for(;;){
    vTaskDelay(10000);
  }
}

void FrontLight_Task(void *parameters){
  animStatus.busy = false;
  ANIMATION_EVENT whiteMsg;
  for(;;){
    xQueueReceive( whiteQueue, &whiteMsg, portMAX_DELAY );

    int msFillTime = 0;
    if(whiteMsg.countDown > 0){ // use countdown if available 
      msFillTime = whiteMsg.countDown;
    }else{
      msFillTime = map(whiteMsg.param1, 0, 100, 0, 10000);
      if(msFillTime < 1000){ msFillTime = 1000; }
    }
    float delayFactor = (float)map(whiteMsg.param2, 0, 100, 0, 100) / 100.0;
    Const_White_Fill(whiteStatus, animProps.frontLight, delayFactor, msFillTime, 50);
  }
}

void load_animation_profile(void){
  File file = LittleFS.open("/cfg/anim-profile-common.json");

  if(!file){
    ESP_LOGE(tag, "Error opening anim-profile-common.json...");
    file.close();
  }else{
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, file);
    file.close();
    if(error){ ESP_LOGE(tag, "anim-profile-common.json deserialize error!.."); return;}

    JsonObject frontJson = doc["countdown"];
    animProps.frontLight.minDuty = jsonConstrain<float>(tag, frontJson, "min", 0.0f, 99.0f, 20.0f);
    animProps.frontLight.maxDuty = jsonConstrain<float>(tag, frontJson, "max", 1.0f, 100.0f, 99.0f);
    animProps.frontLight.holdTime = jsonConstrain<int>(tag, frontJson, "hold", 100, 5000, 1000);
    animProps.frontLight.rampTime = jsonConstrain<int>(tag, frontJson, "ramp", 100, 5000, 500);

    animProps.profileIndex = jsonConstrain<int>(tag, doc.as<JsonObject>(), "profile-index", 0, 7, 0);

    ESP_LOGI(tag, "anim-profile-common.json settings loaded.");
  }
}

void load_animation_profileByIndex(int index){

  //for(int index = 1; index <= 8; index++){
    String strFileName = "/cfg/anim-profile" + String(index + 1) + ".json";
    File file = LittleFS.open(strFileName);

    if(!file){
      ESP_LOGE(tag, "%s", "Error opening " + strFileName);
      file.close();
    }else{
      JsonDocument doc;
      DeserializationError error = deserializeJson(doc, file);
      file.close();
      if(error){ ESP_LOGE(tag, "%s deserialize error!..", strFileName.c_str()); return;}

      JsonObject profJson = doc.as<JsonObject>();
      animProps.profileName = jsonConstrainString(tag, profJson, "name", "").c_str();
      ESP_LOGD(tag, "Profile Index: %d,  Name: %s", index, animProps.profileName);

      //
      JsonArray eventsJson = profJson["events"];
      for(int i = 0; i < animStatus.EventsCount; i++){
        animProps.event[i].selfIndex = i; // Self Index

        if(i == 0){ 
          animProps.event[i].animIndex = jsonConstrain<int>(tag, eventsJson[i], "anim", 0, animProps.whitefillsCount-1, 0); 
        }else{ 
          animProps.event[i].animIndex = jsonConstrain<int>(tag, eventsJson[i], "anim", 0, animProps.animationsCount-1, 0); 
        }

        animProps.event[i].hue = jsonConstrain<int>(tag, eventsJson[i], "hue", -2, 360, 0);
        animProps.event[i].hueRange = jsonConstrain<int>(tag, eventsJson[i], "hue-range", 0, 360, 1);
        animProps.event[i].speed = jsonConstrain<int>(tag, eventsJson[i], "speed", 0, 100, 50);
        animProps.event[i].param1 = jsonConstrain<int>(tag, eventsJson[i], "param1", 0, 100, 50);
        animProps.event[i].param2 = jsonConstrain<int>(tag, eventsJson[i], "param2", 0, 100, 50);
        animProps.event[i].check1 = jsonConstrainBool(tag, eventsJson[i], "check1", false);
        animProps.event[i].check2 = jsonConstrainBool(tag, eventsJson[i], "check2", false);
        animProps.event[i].check3 = jsonConstrainBool(tag, eventsJson[i], "check3", false);
        animProps.event[i].check4 = jsonConstrainBool(tag, eventsJson[i], "check4", false);

        ESP_LOGD(tag, "  self: %d,  anim: %d, param1: %d, speed: %d", animProps.event[i].selfIndex, animProps.event[i].animIndex, animProps.event[i].param1, animProps.event[i].speed);
      }
      ESP_LOGI(tag, "%s settings loaded.", strFileName.c_str());
    }
  //}
}

void RunWhitefillAnimation(ANIMATION_EVENT &event){
  //Start Constant Light Task if available
  if( FrontLight_Task_Handle ){ 
    xQueueSend(whiteQueue, &event, 100); // queue new event
    xTaskNotifyGive( FrontLight_Task_Handle ); 
  }
  
  int ret;
  switch(event.animIndex){
    case 0: // Bottom/Up Fill
      ESP_LOGD(tag, "   Running WhiteFill: %d , bottom/up", event.animIndex);
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
    case 1: // Snake Fill
      ESP_LOGD(tag, "   Running WhiteFill: %d , Snake fill", event.animIndex);
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
    case 2: // Tick Fill
      ESP_LOGD(tag, "   Running WhiteFill: %d , Tick fill", event.animIndex);
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
    case 3: // Smooth Brighten
      ESP_LOGD(tag, "   Running WhiteFill: %d , Smooth Brighten", event.animIndex);
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
    case 4: // Cycle All
      ESP_LOGD(tag, "   Running WhiteFill: %d , Cycle All", event.animIndex);
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
    default: // Random Select
      ESP_LOGD(tag, "   Running WhiteFill: default , bottom/up");
      ret = Animation_White_Fill_Mirrored(*strip1, event);
      break;
  }

  // if exited loop naturally... not from a notification so keep waiting
  if(ret == EXIT_FINISHED || ret == EXIT_TIMEOUT){ 
    ESP_LOGD(tag, "Whitefill normal exit... waiting");
    ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); 
  }
}

void RunAnimation(ANIMATION_EVENT &event){
  if(event.animIndex < 80){
    switch(event.animIndex){ // AnimationEventIndex (0-X) 
      case 0: // Rainbow
        ESP_LOGD(tag, "   Running Anim: %d , Rainbow", event.animIndex);
        Animation_Rainbow(*strip1, event, INFINITE_LOOP); 
        break;
      case 1: // Hue Spectrum Mirrored
        ESP_LOGD(tag, "   Running Anim: %d , Hue Spectrum Mirrored", event.animIndex);
        Animation_Hue_Spectrum_Mirrored(*strip1, event, INFINITE_LOOP);
        break;
      case 2: // Color Pulse Cycle
        ESP_LOGD(tag, "   Running Anim: %d , Color Pulse Cycle", event.animIndex);
        Animation_Pulse_Color_Cycling(*strip1, event);
        break;
      case 3: // Comets Mixed Colors    
        ESP_LOGD(tag, "   Running Anim: %d , Comets", event.animIndex);
        Animation_Comets(*strip1, event, DT_BOTH, INFINITE_LOOP);
        break;
      case 4: // Dashes Single and Mixed Colors
        ESP_LOGD(tag, "   Running Anim: %d , Dashes", event.animIndex);
        Animation_Dashes(*strip1, event, DT_BOTH, INFINITE_LOOP);
        break;
      case 5: // Snakes Single and Mixed Colors
        ESP_LOGD(tag, "   Running Anim: %d , Snakes", event.animIndex);
        Animation_Snakes(*strip1, event,  DT_BOTH,  INFINITE_LOOP);
        break;
      case 6: // Sectors Mixed Colors (No Sigle Colors because pointless)
        ESP_LOGD(tag, "   Running Anim: %d , Sectors", event.animIndex);
        Animation_Sectors(*strip1, event, DT_BOTH, INFINITE_LOOP);
        break;
      case 7: // Fire (Red)
        ESP_LOGD(tag, "   Running Anim: %d , Fire", event.animIndex);
        Animation_Fire(*strip1, event);
        break;
      case 8: //
        break;
      case 9: // Color Strobing
        break;
      case 10: // Random Color Twinkle
        break;
      case 11: // Stacking
        break;
      case 12: // Rain
        break;
      case 13: // Stacking
        break;
      case 14: // RED/WHITE/BLU Flag (density selects color pallet) (sector/comets,dashes, snakes)
        break;
      case 15:
        break;
      default:
        break;
    }
  }else{  // Non Looping Functions
    switch(event.animIndex){ // AnimationEventIndex (0-X) 
      case 80: // clear strip
        strip1->fill(col_black, 0, strip1->effSize);
        strip1->show(true);
        ESP_LOGD(tag, "Animation Strip1 Clear");
        ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
        break;
      case 81: // set pixel
        strip1->setPixel(event.param1, GetColorFromHue(event.hue));
        strip1->show(true);
        ESP_LOGD(tag, "Animation Set Pixel: %d", event.param1);
        ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
        break;
      case 82: // 
        break;
      default:
        break;
    } 
  }

}

// These animations should be quick and not infinite
void RunPopUpAnimations(ANIMATION_EVENT &event){

  switch(event.animIndex){ // AnimationEventIndex (0-X) 
    case 0: // Start/Boot/Power Up Sequence
      ANIMATION_EVENT ev;
      ev.animIndex = 0;
      ev.hue = -1; //{200, 200, 200}; //col_white;
      ev.hueRange = 1; //col_black;
      ev.countDown = 6000; // 6 seconds
      ev.param2 = 75;
      if( FrontLight_Task_Handle ){ 
        xQueueSend(whiteQueue, &ev, 100); // queue new event
        xTaskNotifyGive( FrontLight_Task_Handle ); 
      }
      Animation_White_Fill_Mirrored(*strip1, ev);

      break;
    case 1: // BLE Connected
      break;
    case 2: // BLE Disconnected
      break;
    case 3: // WiFi Connected
      break;
    case 4: // WiFi Disconnected
      break;
    case 5: // Luma Stick Connected
      break;
    default: 
      break;
  }

}



// json color to rgbPixel conversion
// Unused now
rgbpixel_t hexToRGB(const char* hexColor) {
  long hexValue = strtol(hexColor + 1, nullptr, 16);  // Skip the '#' character
  rgbpixel_t pix;
  pix.red = (hexValue >> 16) & 0xFF;
  pix.grn = (hexValue >> 8) & 0xFF;
  pix.blu = hexValue & 0xFF;
  return pix;
}




