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


import { YServo } from 'yoctolib-esm/yocto_servo.js'
import {
  StreamDeckAction,
  YoctopuceDeviceHandler,
  ControllerEnum,
  PluginDispatcher
} from '../common_code/yoctopuce_plugin.js'
import { rectangularGauge } from '../common_code/icons.js'
import { servoSettings } from './servo_settings.js'
import { YAPI, YFunction} from "yoctolib-esm/yocto_api";


export class  StreamDeck_YServoAction extends StreamDeckAction
 {  public  static readonly  ManagedFunctionType = "servo"
    static  get actionUUID() : string {return "com.yoctopuce."+StreamDeck_YServoAction.ManagedFunctionType+ ".action"; }
    public  get ManagedFunctionType() { return   StreamDeck_YServoAction.ManagedFunctionType }
    private  currentSettings   : servoSettings = new servoSettings();
    private  servo : YServo | null =null;
    private  lastValue : number = YServo.POSITION_INVALID;
    private  bg_LOOP     : HTMLImageElement = null as any;
    private  lastImage: HTMLImageElement = null as any ;
    private  gauge : rectangularGauge= null as any ;
    private  lastEncoderSetTime : number =0;
    private  lastEncoderSetValue : 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_YServoAction.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("servo_action.svg")
     this.lastImage = await StreamDeckAction.newImage("servo_action.svg")
   }

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

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

   protected  async functionArrival(hwdname:string)
   { if (this.servo)
   {
     this.valueChanged(this.servo, await this.servo.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.servo!=null)
       if (await this.servo.isOnline())
         switch  (this.currentSettings.action)
         {   case "MOMENTARY":  await this.servo.move(10*this.currentSettings.targetmin,this.delaytotarget( this.currentSettings.targetmin) );  break;
         }
   }

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

   public async onKeyUp(context: any, settings: any, coordinates: any, userDesiredState: any)
   {

     if (this.servo != null)
       if (await this.servo.isOnline())
         switch (this.currentSettings.action)
         {
           case "MOVE"          :
             await this.servo.move(10*this.currentSettings.targetmin, this.delaytotarget( this.currentSettings.targetmin));
             break;
           case "MOMENTARY"     :
             await this.servo.move(10*this.currentSettings.targetmax, this.delaytotarget( this.currentSettings.targetmax));
             break;
           case "TOGGLE"        :
             if (this.lastValue == YServo.POSITION_INVALID) this.lastValue = await this.servo.get_position();
             if  (this.lastValue != YServo.POSITION_INVALID)
             {  let median: number = (this.currentSettings.targetmin + this.currentSettings.targetmax) / 2;
               let min: number = this.currentSettings.targetmin < this.currentSettings.targetmax ? this.currentSettings.targetmin : this.currentSettings.targetmax;
               let max: number = this.currentSettings.targetmin > this.currentSettings.targetmax ? this.currentSettings.targetmin : this.currentSettings.targetmax;
               if (this.lastValue < median) await this.servo.move(10*max, this.delaytotarget(max));
               else await this.servo.move(10*min, this.delaytotarget(min));
             } else await this.servo.move(10*this.currentSettings.targetmin, this.delaytotarget( this.currentSettings.targetmin));
             break;
         }
   }

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

   private incrementRound(value:number) :number
   {
     return  Math.round(value / (10*this.currentSettings.increment)  ) * (10*this.currentSettings.increment);
   }

   public async dialRotate (context: any, settings: any, coordinates: any,  ticks:number, pressed:boolean)
   { let now : number =  await YAPI.GetTickCount();
     let newValue : number;
     let inc :number = 10*this.currentSettings.increment* Math.sign(ticks) * ticks*ticks;
     if  ((now- this.lastEncoderSetTime)   <250)
        {  newValue  =  this.incrementRound (  this.lastEncoderSetValue + inc)  ;
          this.encoderRotate(newValue)
          this.lastEncoderSetTime = now;
        }
      else
       {  newValue =   this.incrementRound ( this.lastValue + inc)  ;
         this.encoderRotate(newValue)  ;
       }
     this.lastEncoderSetValue = newValue;
   }

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

   // called from top whenever a Servo value has changed
   public valueChanged(source: YServo, value: string)
   {
     if (source == this.servo)
     { let fvalue: number = parseFloat(value);
       if (fvalue < -1000) fvalue = 1000;
       if (fvalue >  1000) fvalue = 1000;
       this.lastValue = fvalue;
       this.gauge.setValue( fvalue / 10.0, (fvalue/10.0).toFixed(1)+"%" )
       this.redrawIcon();
     }
   }

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

   protected async redrawIcon()
   {
     if (this.servo != null)
     { let online: boolean = await this.servo.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.gauge.offline = !online;
       let url:string = this.gauge.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.servo = await YServo.FindServo(this.currentSettings.hwdName);
       await this.servo.registerValueCallback((source:YFunction,value:string)=>{this.broadcastValueChange(source,value);})
       await this.redrawIcon();
     } else  console.log("servo 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.gauge = new rectangularGauge(200, 100, 100, 52, 100, 24, 0,  "%")
     else
       this.gauge = new rectangularGauge(72, 72, 36, 20, 60, 15,  0, "%")
     await this.didReceiveSettings(context, settings, coordinates)
   }

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

   private async encoderRotate(value :number)
   { if (value < -1000) value = -1000;
     if (value > 1000) value = 1000;
     if (this.servo != null)
       if (await this.servo.isOnline())
         await this.servo.set_position(value);
   }

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

 }

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

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




