//Main object of the API that can handle all the traffic from/to the server to/from the web.
function PAPI_Handler() {
  /*************************************************************************
   **************** PRIVATE VARIABLES AND METHODS ***************************
   *************************************************************************/
  //Private Promax handler
  var handler = new PAPI_P_Handler();

  /*************************************************************************
   ******************** PUBLIC METHODS **************************************
   *************************************************************************/

  this.moni = new PAPI_moni_Handlder(handler);
  this.schedulingTask = new PAPI_tasksched_Handler(handler);

  //CALLBACKS
  //Register events from VIEW, i.e., callbacks that will be called when the event occur.
  /** Callback done when all sockets are correctly opened and authenticated to the server*/
  this.registerEvent_onConnectionOpen = function (func) {
    handler.registerEvent_onConnectionOpen(func);
  };

  /** Callback done when communication is reseted*/
  this.registerEvent_onConnectionClose = function (func) {
    handler.registerEvent_onConnectionClose(func);
  };

  /** Callback done when an error occurred during connexion*/
  this.registerEvent_onConnectionError = function (func) {
    handler.registerEvent_onConnectionError(func);
  };

  /** Callback done when an error happened... now used for debugging purposes mostly*/
  this.registerEvent_onError = function (func) {
    handler.registerEvent_onError(func);
  };

  /** Callback done when server provieded new data JSON, IMG...*/
  this.registerEvent_onNewData = function (func) {
    handler.registerEvent_onNewData(func);
  };

  /** TBD*/
  this.registerEvent_onACK = function (func) {
    handler.registerEvent_onACK(func);
  };

  /** Callback done on succesful authentication*/
  this.registerEvent_onAuth = function (func) {
    handler.registerEvent_onAuth(func);
  };

  /** Callback done on succesful pwd changed*/
  this.registerEvent_onPwdChanged = function (func) {
    handler.registerEvent_onPwdChanged(func);
  };

  //METHODS
  /* Open the necessary connections to make
	the view work under the given reason*/
  this.connect = function (reason) {
    switch (reason) {
      case PAPI_connection_reasons.AUTH:
        handler.onNewSession();
        break;
      case PAPI_connection_reasons.IMG:
        handler.onIMG();
        break;
      case PAPI_connection_reasons.TAB_SP:
        handler.onSP();
        break;
      case PAPI_connection_reasons.TAB_TV:
        handler.onTV();
        break;
      case PAPI_connection_reasons.TAB_EMU:
        handler.onEMU();
        break;
      case PAPI_connection_reasons.TAB_MONI:
        handler.onMONI('MON');
        break;
      case PAPI_connection_reasons.TAB_SPMONI:
        handler.onMONI('SPMON');
        break;
      case PAPI_connection_reasons.TAB_HIST:
        handler.onHIST();
        break;
      case PAPI_connection_reasons.TAB_FILE_MGR:
        handler.onINST();
        break;
      case PAPI_connection_reasons.CHANGE_PWD:
        handler.onNewPwd();
        break;
      default:
        //Not to be reached...so debugging prupose
        alert('Connection reason not recognized');
    }
  };

  /* Close the connections according to the
	given reason*/
  this.disconnect = function (reason) {
    switch (reason) {
      case PAPI_connection_reasons.IMG:
        handler.onStopIMG();
        break;
      case PAPI_connection_reasons.ALL:
        handler.closeAll();
        break;
      case PAPI_connection_reasons.LOGOUT:
        handler.closeAll();
        resetSId();
        break;
      default:
        //Not to be reached...so debugging prupose
        alert('Connection reason not recognized');
    }
  };

  //Calls the canged password function
  //@param recovery = true if its authenticating with the recovery pwd
  this.changePwd = function (data, recovery) {
    handler.changePwd(data, recovery);
  };

  //Get JSON before authentication
  this.get_unauth_data = function () {
    handler.get_unauth_data();
  };

  //Authenticates
  //@param recovery = true if its authenticating with the recovery pwd
  this.auth = function (data, recovery) {
    handler.auth(data, recovery);
  };

  //Gets the pwd recovery code that should be entered in the app
  this.get_pwd_recovery_code = function () {
    return handler.get_pwd_recovery_code();
  };

  //Send data to the Server (expecting an object of type PL_newClientData)
  this.send = function (data) {
    handler.send(data);
  };

  /** Called when a key is pressed.
   *@param keyName, one of PAPI_key_names
   *@param lonPress true or false
   */
  this.onKeyPress = function (keyName, longPress) {
    var type;

    if (longPress) {
      type = longPress;
    }

    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, 'KEY', ' NAME=' + keyName + ' TYPE=' + type));
  };

  /** Called when a touch is pressed.
   *@param x coordinate
   *@param y coordinate
   */
  this.onTouchPress = function (x, y) {
    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, 'TOUCH', ' X=' + x + ' Y=' + y));
  };

  //Starts/stops emulation
  this.startEmu = function (start) {
    if (start == true) {
      handler.startStreaming();
    } else {
      handler.stopStreaming();
    }
  };

  //Starts/stops audio
  this.startAudio = function (obj) {
    handler.startAudio(obj);
  };

  this.startETIRecord = function (obj) {
    handler.startETIRecord(obj);
  };

  /** Starts TS Record
   * @param description of the file where the record will be saved
   * @param servideRecord true if it is service recording, false otherwise
   * @param useUSB = true if the file must be stored in the Flash memory (not in usb).
   */

  this.startTSRecord = function (description, serviceRecord, memory) {
    var command = ' START ';

    if (serviceRecord) {
      command = ' START_SERV ';
    }

    command += ' DESC=' + description;

    switch (memory) {
      case 'hd':
        command += ' UNIT=HD';
        break;
      case 'usb':
        command += ' UNIT=USB';
        break;
    }

    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, 'TSRECORD', command));
  };

  this.startStreamingTS = function (enabled, ip, port, tsStreaming) {
    handler.startStreamingTS(enabled, ip, port, tsStreaming);
  };

  this.checkTSRecord = function (serviceRecord, memory) {
    var command = ' START ';

    if (serviceRecord) {
      command = ' START_SERV ';
    }

    switch (memory) {
      case 'hd':
        command += ' UNIT=HD';
        break;
      case 'usb':
        command += ' UNIT=USB';
        break;
    }

    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, '?TSRECORD', command));
  };

  /** Stops TS Record */
  this.stopTSRecord = function () {
    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, 'TSRECORD', ' STOP'));
  };

  this.reboot = function () {
    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, 'EQUIPMENT', ' RESTART'));
  };

  this.getJSONInfo = function () {
    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, '?INFO'));
  };

  this.openBasicSockets = function () {
    handler.openBasicSockets();
  };

  this.changeInstallation = function (installation) {
    handler.send(new PAPI_newClientData(PAPI_data_type.CMD, '?INSTALLATION', ' NAME=' + installation + ' LOAD'));
  };

  /* Given a PAPI_inputValidationData it checks if the input fullfills
	the format criteria and return an object of type PAPI_inputValidationResult
	with the results of the evaluation*/
  this.checkInput = function (input) {
    var retData;
    var max = parseFloat(input.max, 10);
    var min = parseFloat(input.min, 10);
    input.units = input.units.replace('µ', 'u'); //Replace μ
    input.units = input.units.replace('k', 'K'); //Replace k
    var unitsNoFactor = input.units.replace(/[GMKk]/, '');
    unitsNoFactor = new RegExp(unitsNoFactor.toUpperCase());

    input.value = input.value.replace(/ +/g, ''); //Remove spaces
    input.value = input.value.replace(/,+/g, '.'); //Change , for .
    input.value = input.value.toUpperCase(); //Capitalize

    var unitsPresent = input.value.match(unitsNoFactor);
    input.value = input.value.replace(unitsNoFactor, ''); //Remove units

    //Regular expressions to look for
    //http://regexr.com/
    var onlyNum = /^-?[\d .]{1,9}$/;
    onlyNum = onlyNum.test(input.value);
    var NumFactor = /^[\d.]{1,9}[GMK]{1}$/;
    NumFactor = NumFactor.test(input.value);

    if (!onlyNum && !NumFactor) {
      //Throw error.
      return new PAPI_inputValidationResult(false, 'Incorrect format');
    }

    //Correct format
    num = parseFloat(input.value, 10);

    if (onlyNum && !unitsPresent) {
      //Add default factor
      input.value = input.value + input.units;
    }

    //Remove factor
    if (input.value.search('G') > 0) {
      num = num * 1.0e9;
    } else if (input.value.search('M') > 0) {
      num = num * 1.0e6;
    } else if (input.value.search('K') > 0) {
      num = num * 1.0e3;
    }

    //Get correct max and min
    if (input.units.search('G') >= 0) {
      max = max * 1.0e9;
      min = min * 1.0e9;
    } else if (input.units.search('M') >= 0) {
      max = max * 1.0e6;
      min = min * 1.0e6;
    } else if (input.units.search('K') >= 0) {
      max = max * 1.0e3;
      min = min * 1.0e3;
    }

    if (num < min || num > max) {
      //Throw PAPI error.
      retData = new PAPI_inputValidationResult(false, 'Value out of range.');
    } else {
      retData = new PAPI_inputValidationResult(true, num.toString());
    }

    return retData;
  };
}

//Reasons that can be used to connect
var PAPI_connection_reasons = Object.freeze({
  AUTH: 'AUTH', //Open new session
  TAB_SP: 'SP', //SP and measures tab
  TAB_TV: 'TV', //TV tab
  TAB_EMU: 'EMU', //Tab emulating equipment
  TAB_MONI: 'MONI', //Monitoring tab
  TAB_SPMONI: 'SPMONI', //Monitoring tab
  TAB_HIST: 'HIST', //Monitoring historical tab
  TAB_FILE_MGR: 'FILEMGR', //File Mgr tab
  TAB_TASKPLANNER: 'TASKPLANNER', //Taskmanager tab
  IMG: 'IMG', //Show/hide images
  CHANGE_PWD: 'PWD', //Change pwd
  ALL: 'ALL', //Close all sockets
  LOGOUT: 'LOGOUT', //LOGOUT
});

//Reason of the triggered errors
var PAPI_error_reasons = Object.freeze({
  AUTH: 0, //missing authentication
  IMG: 1, //Problems getting the IMG
  CMD: 2, //Wrong cmd sent
  JSON: 3, //Wrong remote cmd sent
  CONNECTION: 4, //Problems with the connection
});

//Data types that can arrive in a NewData callback or can be sent via the send function to the server.
var PAPI_data_type = Object.freeze({
  AUTH: 0, //pwd (send)
  CMD: 1, //Remote command (send)
  JSON_AUTH: 2, //New JSON_AUTH
  IMG: 3, //New img
  JSON_SP: 4, //New JSON_SP
  JSON_CONFIG: 5, //New JSON_CONFIG
  JSON_MONI_STATIC: 6, //New JSON MONI STATIC
  JSON_MONI_MEAS: 7, //New JSON MONI MEAS
  JSON_MONI_ALARM: 8, //New JSON MONI ALARM
  JSON_MONI_TIME: 9, //New JSON MONI TIME
  JSON_SPMONI: 10, //New JSON SP MONI
  JSON_SPMONI_REFERENCE: 11, //New JSON SP MONI REFERENCE
  REMOTE_JSON: 12, //New REMOTE JSON
  JSON_INFO: 13, //New JSON INFO
  PING: 14, //Keep connection alive
});

//Error messges that must be notified to the user. TODO: Use correct language
var PAPI_error_messages = Object.freeze({
  PWD_WRONG: 'Wrong Password.',
  DEVICE: "Can't connect to device.",
  PWD_MISMATCH: "Entered passwords aren't the same.",
  PWD_SHORT: 'The new password is too short. Enter at least six characters.',
  PWD_CHANGED: 'Password changed succesfully.',
  TIMEOUT: 'Session expired, disconnected by timeout.',
  SESSION_LIMIT: 'Max number of sessions reached.',
  LOGOUT: 'The equipment is not controlled remotely anymore. Please close this window to LogIn again.',
});

//Names of the keys
var PAPI_key_names = Object.freeze({
  MEAS: 'MEAS',
  SP: 'SPECTRUM',
  TV: 'TV',
  FOTO: 'FOTO',
  F1: 'SOFTKEY0',
  F2: 'SOFTKEY1',
  F3: 'SOFTKEY2',
  F4: 'SOFTKEY3',
  FOLDER: 'MENU_A',
  TOOL: 'MENU_B',
  RIGHT: 'RIGHT',
  LEFT: 'LEFT',
  UP: 'UP',
  DOWN: 'DOWN',
  CENTER: 'ENTER',
});

//DATA OBJECTS FOR THE CALLBACKS
//Object to handle the reconnection info in case of a protocol closure
function PAPI_reconnect(reconnect, ask) {
  this.reconnect = reconnect; //true or false depending if we want reconnection
  this.ask = ask; //true if we must ask the user what to do
}

//Object to handle errors. Used as parameter on the onError callback
function PAPI_error(reason, data) {
  this.reason = reason; // One of PAPI_error_reasons
  this.data = data;
}

//Object to handle new arrived data. Used as parameter on the onNewData callback
function PAPI_newData(type, data) {
  this.type = type; // One of PAPI_data_type
  this.data = data;
}

//Object to handle the data that has to be send to the server
function PAPI_newClientData(type, id, param) {
  this.type = type; // One of PAPI_data_type (send)
  this.id = id;
  this.value = param;

  this.add_field = function (id, param) {
    if (this.value == undefined) {
      this.value = id + param;
    } else {
      this.value = this.value + id + param;
    }
  };
}

//Object to handle the data validation
function PAPI_inputValidationData(value, min, max, units) {
  this.value = value;
  this.max = max;
  this.min = min;
  this.units = units;
}

//Object that keeps the result of the data validation
function PAPI_inputValidationResult(success, value) {
  this.value = value;
  this.success = success;
}
