/*********************************************************************
 *
 *  $Id: app.ts 32624 2018-10-10 13:23:29Z seb $
 *
 *  Yoctopuce Plugin to ELGATO  StreamDeck
 *
 *
 *********************************************************************/


import { YColorLedCluster } from 'yoctolib-esm/yocto_colorledcluster.js'
import {
  StreamDeckAction,
  YoctopuceDeviceHandler,
  ControllerEnum,
  PluginDispatcher
} from '../common_code/yoctopuce_plugin.js'
import {CircularGauge, genericSVGIcon} from '../common_code/icons.js'
import { colorLedClusterSettings } from './colorLedCluster_settings.js'
import { YAPI, YFunction} from "yoctolib-esm/yocto_api";
import {promises} from "dns";


export class  StreamDeck_YColorLedClusterAction extends StreamDeckAction
 {  public  static readonly  ManagedFunctionType = "colorLedCluster"
    static  get actionUUID() : string {return "com.yoctopuce."+StreamDeck_YColorLedClusterAction.ManagedFunctionType+ ".action"; }
    public  get ManagedFunctionType() { return   StreamDeck_YColorLedClusterAction.ManagedFunctionType }
    private  currentSettings   : colorLedClusterSettings = new colorLedClusterSettings();
    private  cluster : YColorLedCluster | null =null;
    private  bg_LOOP     : HTMLImageElement = null as any;
    private  lastImage: HTMLImageElement = null as any ;
    private  icon : smartRgbLedIcon= null as any ;
    private  lastEncoderSetTime : number =0;
    private  lastEncoderSetValue : number =0;
    private  lastColor  : number=0;
    private  lastHSLsetValue :number=0;



   /*******************************
    * notify the system which Yoctopuce function type is handled by the plugin
    *  must be called at at application start
    *******************************/

    public  static registerFunctionType()
    {
      YoctopuceDeviceHandler.registerFunctionType(StreamDeck_YColorLedClusterAction.ManagedFunctionType);
    }

   /*******************************
    * constructor, auttomatically called when a new action is created
    *******************************/

    constructor(uuid:string)
    {   super(uuid);
        YoctopuceDeviceHandler.registerAction(this.ManagedFunctionType,this);
    }

   /*******************************
    * local init
    *******************************/

   public async init()
   { this.bg_LOOP = await StreamDeckAction.newImage("colorLedCluster_action.svg")
     this.lastImage = await StreamDeckAction.newImage("colorLedCluster_action.svg")
   }

   /*******************************
    * return actions settings
    *******************************/
   protected get settings(): colorLedClusterSettings{return this.currentSettings; }

   /*******************************
    * called when the device linked to the action is appearing
    *******************************/

   protected  async functionArrival(hwdname:string)
   { if (this.cluster)
    {
       this.valueChanged(this.cluster, await this.cluster.get_advertisedValue());
   }}

   /*******************************
    * called when the device linked to the action is disappearing
    *******************************/

   protected  async functionRemoval(hwdname:string)
   { this.redrawIcon(); }

   /*******************************
    * called when the key or rotary button is pressed down
    *******************************/

   public  async onKeyDown(context: any, settings: any, coordinates: any, userDesiredState: any)
   {
     if (this.cluster!=null)
       if (await this.cluster.isOnline())
         switch  (this.currentSettings.action)
         {   case "MOMENTARY":  await this.move(this.currentSettings.color1);  break;
         }
   }

   private HTMLcolor2RGBint(color:string) : number
   {  if (color.substring(0,1)=="#") color = color.substring(1);
      return   parseInt("0x"+color);

   }

   public async move(color :string )
   {
     let value :number  =this.HTMLcolor2RGBint(color)
     if (this.cluster)
     { if (this.settings.colorSpace=="HSL") this.cluster.hsl_move( this.currentSettings.firstLed,this.currentSettings.ledCount,  this.computeHSL(value),this.currentSettings.transitionDelay );
       else  this.cluster.rgb_move( this.currentSettings.firstLed,this.currentSettings.ledCount,  value,this.currentSettings.transitionDelay );
   }}

   /*******************************
    * called when the key or rotary button is pressed up
    *******************************/

   public async onKeyUp(context: any, settings: any, coordinates: any, userDesiredState: any)
   {
     let c2 :number  =this.HTMLcolor2RGBint(this.currentSettings.color2);
     let color2:string = this.currentSettings.color2;
     if (this.cluster != null)
       if (await this.cluster.isOnline())
         switch (this.currentSettings.action)
         {  case "MOMENTARY"     :
             await this.move(this.currentSettings.color2);
             break;
           case "ONOFF"         :
             c2=0;
             color2="#000000"
           case "TOGGLE"        :
             let c1 :number  =this.HTMLcolor2RGBint(this.currentSettings.color1)
             let totalDist = this.RGBDistance(c1,c2);
             let c1Dist = this.RGBDistance(c1,this.lastColor);
             if (c1Dist <  totalDist/2) await this.move(color2);
                                         else  await this.move(this.currentSettings.color1);
             break;
         }
   }

   private RGBDistance(c1:number,c2:number)
   { let r1 : number= (c1 & 0xFF0000) >> 16
     let r2 : number= (c2 & 0xFF0000) >> 16
     let g1 : number= (c1 & 0x00FF00) >> 8
     let g2 : number= (c2 & 0x00FF00) >> 8
     let b1 : number= (c1 & 0x0000FF)
     let b2 : number= (c2 & 0x0000FF)
     let r : number = r2-r1;
     let g : number = g2-g1;
     let b : number = b2-b1;
     return Math.sqrt( r*r + g*g + b*b    );
   }


   /*******************************
    * called the button was turned
    *******************************/

   public async dialRotate (context: any, settings: any, coordinates: any,  ticks:number, pressed:boolean)
   {
     let action : string = this.currentSettings.encoderAction;

     if (action=="R" || action=="G" || action=="B")
     {
       let r: number = (this.lastColor >> 16) & 0xFF;
       let g: number = (this.lastColor >> 8) & 0xFF;
       let b: number = this.lastColor & 0xFF;
       switch (action)
       {
         case "R" :
           r = Math.max(0, Math.min(255, r + ticks * ticks));
           break;
         case "G" :
           g = Math.max(0, Math.min(255, g + ticks * ticks));
           break;
         case "B" :
           b = Math.max(0, Math.min(255, b + ticks * ticks));
           break;
       }

       let value: number = (r << 16) | (g << 8) | b;
       if (this.cluster)
       {
         this.cluster.set_rgbColor(this.currentSettings.firstLed, this.currentSettings.ledCount, value);

       }
     }else
     { let hsl:number = this.computeHSL(this.lastColor);
       let h: number = (hsl >> 16) & 0xFF;
       let s: number = (hsl >> 8) & 0xFF;
       let l: number =  hsl & 0xFF;
       switch (action)
       {
         case "H" :
           h = Math.max(0, Math.min(255, h + ticks * ticks));
           break;
         case "S" :
           s = Math.max(0, Math.min(255, s + ticks * ticks));
           break;
         case "L" :
           l = Math.max(0, Math.min(255, l + ticks * ticks));
           break;
       }

       this.lastHSLsetValue = (h << 16) | (s << 8) | l;
       if (this.cluster)
       {
         this.cluster.set_hslColor(this.currentSettings.firstLed, this.currentSettings.ledCount, this.lastHSLsetValue);

       }
     }



   }

   /*******************************
    * called the device current value has changed
    *******************************/

   // called from top whenever a colorLedCluster value has changed
   public async valueChanged(source: YColorLedCluster, value: string)
   {
     if (source == this.cluster)
     {

       this.lastColor = await this.currentColor();
       this.icon.set_rgbColor(this.lastColor );
       this.redrawIcon();
     }
   }

  private async currentColor():Promise<number>
  { if  (this.cluster!=null)
    { let RGBvalue: number[] = await this.cluster.get_rgbColorArray(this.currentSettings.firstLed, 1);
      return RGBvalue[0];
    }
    return 0;
  }





   /*******************************
    * redraw the action icon
    *******************************/

   protected async redrawIcon()
   {
     if (this.cluster != null)
     { let online: boolean = await this.cluster.isOnline()
       this.lastImage = new Image();
       this.lastImage.onload = () =>
       { if (this.controller == ControllerEnum.ENCODER)
          { let image :string =   PluginDispatcher.HTMLImageElement2url( this.lastImage , null);
            PluginDispatcher.setFeedback(this.UUID, {"full-canvas": image })
          }
         else this.setBgImage(this.UUID, this.lastImage)
       }
       this.icon.offline = !online;
       let url:string = this.icon.asURL
       this.lastImage.src =url ;
     } else this.setBgImage(this.UUID, this. bg_LOOP )

   }

   /*******************************
    * called with local settings
    *******************************/
   public async didReceiveSettings(context: any, settings: any, coordinates: any)
   {
     super.didReceiveSettings(context, settings, coordinates)
     if (this.currentSettings.hwdName!="")
     {   console.log("name= " + this.currentSettings.hwdName + " action=" + this.currentSettings.action );
       this.cluster = await YColorLedCluster.FindColorLedCluster(this.currentSettings.hwdName);
       await this.cluster.registerValueCallback((source:YFunction,value:string)=>{this.broadcastValueChange(source,value);})
       await this.redrawIcon();
     } else  console.log("colorLedCluster name not set");
   }

   /*******************************
    * called with global settings
    *******************************/

   public async didReceiveGlobalSettings(context: any,  settings: any){  }

   /*******************************
    * called when the action is about to be shown
    *******************************/

   public async onWillAppear(context: any, controller: ControllerEnum, settings: any, coordinates: any)
   {
     super.onWillAppear(context, controller, settings, coordinates)
     console.log("onWillAppear" + JSON.stringify(settings))
     if (this.controller == ControllerEnum.ENCODER)
       this.icon = new smartRgbLedIcon(200,100)
     else
       this.icon = new smartRgbLedIcon(72,72)
     await this.didReceiveSettings(context, settings, coordinates)

   }

   /*******************************
    * extra stuff
    *******************************/

   private async encoderRotate(value :number)
   {



   }


   public delaytotarget( targetValue:number,distance:number)
   { // let distance= Math.abs(targetValue-this.lastValue)
     //return  this.currentSettings.delay * distance / Math.abs(this.currentSettings.targetmax - this.currentSettings.targetmin)
     return 0;
   }


   private computeHSL(rgb:number): number
   {
     let R = (rgb >> 16) & 0xff;
     let G = (rgb >> 8) & 0xff;
     let B = rgb  & 0xff;
     let H: number;
     let S: number;
     let L: number;
     let max: number = (R > G ? R : G);
     let min: number = (R < G ? R : G);
     let correction: number = 0;
     let divisor: number = 0;

     if (B > max) max = B;
     if (B < min) min = B;

     L = ((max + min + 1) / 2) >> 0;
     if (max == min)
      {
       return L;
      }

     correction = ((max + min) / 2) >> 0;

     if (L <= 127)
     {
       S = ((255 * (max - min) + correction) / (max + min)) >> 0;
     }
     else
     {
       S = ((255 * (max - min) + 255 - correction) / (510 - (max + min))) >> 0;
     }

     correction = 3 * (max - min);
     divisor = 2 * correction;

     if (R == max)
     {
       H = 0;
       R = G;
       G = B;
     }
     else if (G == max)
     {
       H = 85;
       G = R;
       R = B;
     }
     else
     { H = 170; }
     if (R >= G)
     {
       H += ((255 * (R - G) + correction) / divisor) >> 0;
     }
     else
     {
       H += 255 - ((255 * (G - R) - correction) / divisor) >> 0;
     }

     if (H > 255) H -= 255;
     if (S > 255) S = 255; // just in case
     if (L > 255) L = 255;

     return (H<<16 ) | (S<<8) | L;


   }

 }

export class smartRgbLedIcon extends genericSVGIcon
{
  private led: SVGCircleElement ;
  private  RvalueLabel : SVGTextElement | null =null;
  private  GvalueLabel : SVGTextElement | null =null;
  private  BvalueLabel : SVGTextElement | null =null;


  public set_rgbColor(value:number)
  { let color : string  =  "00000" + value.toString(16);
    color = "#"+color.substring(color.length-6);
    this.led.setAttribute("fill",color);
    if  (this.RvalueLabel!=null)
    { let x: number = (value >> 16) & 0xFF;
      let s: string = "0" + x.toString(16);
      this.RvalueLabel.innerHTML = "R: 0x" + s.substring(s.length-2);
    }
    if  (this.GvalueLabel!=null)
    { let x: number = (value >> 8) & 0xFF;
      let s: string = "0" + x.toString(16);
      this.GvalueLabel.innerHTML = "G: 0x" + s.substring(s.length-2);
    }
    if  (this.BvalueLabel!=null)
    { let x: number = value  & 0xFF;
      let s: string = "0" + x.toString(16);
      this.BvalueLabel.innerHTML = "B: 0x" + s.substring(s.length-2);
    }
  }






  private setCommonStyle(e: SVGElement)
  {
    e.setAttribute("stroke-width", "2");
    e.setAttribute("stroke-linecap", "round");
    e.setAttribute("stroke-linejoin", "round");
   // e.setAttribute("stroke-miterlimit", "22.9256");  // ?

  }

  constructor(imgXsize: number, imgYsize: number)
  {
    super(imgXsize, imgYsize)

    let g : SVGGElement =  document.createElementNS("http://www.w3.org/2000/svg", "g") as SVGGElement;
    if (imgXsize>100) g.setAttribute("transform","translate(20 22) scale(1.2 1.2)");
    g.setAttribute("fill","none");
    this.svg.appendChild(g);


    let r: SVGRectElement = document.createElementNS("http://www.w3.org/2000/svg", "rect") as SVGRectElement;
    r.setAttribute("x", "58")
    r.setAttribute("y", "14")
    r.setAttribute("width", "4")
    r.setAttribute("height", "8")
    r.setAttribute("stroke", "#898989");
    r.setAttribute("fill", "#EBECEC");
    this.setCommonStyle(r);
    g.appendChild(r);

    r = document.createElementNS("http://www.w3.org/2000/svg", "rect") as SVGRectElement;
    r.setAttribute("x", "58")
    r.setAttribute("y", "42")
    r.setAttribute("width", "4")
    r.setAttribute("height", "8")
    r.setAttribute("stroke", "#B2B3B3");
    r.setAttribute("fill", "#D9DADA");

    this.setCommonStyle(r);
    g.appendChild(r);

    r = document.createElementNS("http://www.w3.org/2000/svg", "rect") as SVGRectElement;
    r.setAttribute("x", "10")
    r.setAttribute("y", "14")
    r.setAttribute("width", "4")
    r.setAttribute("height", "8")
    r.setAttribute("stroke", "#B2B3B3");
    r.setAttribute("fill", "#D9DADA");
    this.setCommonStyle(r);
    g.appendChild(r);

    r = document.createElementNS("http://www.w3.org/2000/svg", "rect") as SVGRectElement;
    r.setAttribute("x", "10")
    r.setAttribute("y", "42")
    r.setAttribute("width", "4")
    r.setAttribute("height", "8")
    r.setAttribute("stroke", "#B2B3B3");
    r.setAttribute("fill", "#D9DADA");
    this.setCommonStyle(r);
    g.appendChild(r);

    r = document.createElementNS("http://www.w3.org/2000/svg", "rect") as SVGRectElement;
    r.setAttribute("x", "14")
    r.setAttribute("y", "10")
    r.setAttribute("width", "44")
    r.setAttribute("height", "44")
    r.setAttribute("rx", "2")
    r.setAttribute("ry", "2")
    r.setAttribute("stroke", "#898989");
    r.setAttribute("fill", "#EBECEC");
    this.setCommonStyle(r);
    g.appendChild(r);

    let l: SVGLineElement = document.createElementNS("http://www.w3.org/2000/svg", "line") as SVGLineElement;
    l.setAttribute("x1", "14")
    l.setAttribute("y1", "18")
    l.setAttribute("x2", "22")
    l.setAttribute("y2", "10")
    l.setAttribute("stroke", "#898989");
    this.setCommonStyle(l);
    g.appendChild(l);

    this.led = document.createElementNS("http://www.w3.org/2000/svg", "circle") as SVGCircleElement;
    this.led.setAttribute("cx", "36")
    this.led.setAttribute("cy", "32")
    this.led.setAttribute("r", "18")
    this.led.setAttribute("stroke", "#727271");
    this.setCommonStyle( this.led);
    g.appendChild( this.led);

    if (imgXsize>100)
    { let fontSize : number  = 15;
      let  y:  number =35;



      this.RvalueLabel =  document.createElementNS("http://www.w3.org/2000/svg","text") as SVGTextElement;
      this.RvalueLabel.setAttribute("dominant-baseline", "hanging")
      this.RvalueLabel.setAttribute("text-anchor","end")
      this.RvalueLabel.setAttribute("font-family","Arial, Helvetica, sans-serif")
      this.RvalueLabel.setAttribute("font-size",fontSize.toString())
      this.RvalueLabel.setAttribute("x",(imgXsize-5).toString())
      this.RvalueLabel.setAttribute("y",y.toString())
      this.RvalueLabel.setAttribute("fill","white");
      this.RvalueLabel.setAttribute("font-family" ,"monospace");
      this.RvalueLabel.innerHTML="R: 0x00";
      this.svg.appendChild(this.RvalueLabel);
      y+= fontSize*1.2;

      this.GvalueLabel =  document.createElementNS("http://www.w3.org/2000/svg","text") as SVGTextElement;
      this.GvalueLabel.setAttribute("dominant-baseline", "hanging")
      this.GvalueLabel.setAttribute("text-anchor","end")
      this.GvalueLabel.setAttribute("font-family","Arial, Helvetica, sans-serif")
      this.GvalueLabel.setAttribute("font-size",fontSize.toString())
      this.GvalueLabel.setAttribute("x",(imgXsize-5).toString())
      this.GvalueLabel.setAttribute("y",y.toString())
      this.GvalueLabel.setAttribute("fill","white");
      this.GvalueLabel.setAttribute("font-family" ,"monospace");
      this.GvalueLabel.innerHTML="G: 0x00";
      this.svg.appendChild(this.GvalueLabel);
      y+= fontSize*1.2;


      this.BvalueLabel =  document.createElementNS("http://www.w3.org/2000/svg","text") as SVGTextElement;
      this.BvalueLabel.setAttribute("dominant-baseline", "hanging")
      this.BvalueLabel.setAttribute("text-anchor","end")
      this.BvalueLabel.setAttribute("font-family","Arial, Helvetica, sans-serif")
      this.BvalueLabel.setAttribute("font-size",fontSize.toString())
      this.BvalueLabel.setAttribute("x",(imgXsize-5).toString())
      this.BvalueLabel.setAttribute("y",y.toString())
      this.BvalueLabel.setAttribute("fill","white");
      this.BvalueLabel.setAttribute("font-family" ,"monospace");
      this.BvalueLabel.innerHTML="B: 0x00";
      this.svg.appendChild(this.BvalueLabel);

    }

    this.AddOfflineIndicator();
  }
}


/*******************************
 * important!
 *******************************/

StreamDeck_YColorLedClusterAction.registerFunctionType();
// @ts-ignore




