

var g_langSetToDefault   = 'set to default';

function framingInitLanguage( langSetToDefault ) {
    g_langSetToDefault   = typeof langSetToDefault   === 'undefined' ? g_langSetToDefault   : langSetToDefault;
};

var g_frmConstraints = Object.freeze({frmMinWidth:  32,
                                      frmMinHeight: 32,
                                      frmVerticalGrid:  32,
                                      frmHorizontalGrid: 32}); 
      
var g_currentResizeId = '';

var g_scale = 4;

var g_tmpId = 1;

function cloneObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
};
  
class CFrame {
  constructor( parentId, data, parent, id ) {
    this.boCol = false; 
    this.Id     = data.Id     ; 
    this.frmId  = id     ;
    this.Name   = data.Name   ;
    this.width  = data.width  ;
    this.height = data.height ;
    this.left   = data.left   ;
    this.top    = data.top    ;
    this.parentDomId = parentId ;
    this.domId = this.parentDomId + this.frmId;
    this.parent = parent;
    var _self = this;
    this.isNew = data.isNew || false;
    this.removed = data.removed || false;
    this.changed = data.changed || false;
    this.configId = data.configId || 0;
    
    j( '#' + this.parentDomId ).append( 
      '<div id="' + this.domId + '" class="clsFrame frm_draggable">' +
        '<h3 class="ui-widget-header" id="' + this.domId + 'name">' + this.Name + '</h3>' +
      '</div>' );
              
    j( '#' + this.domId )
      .draggable({
        containment: "#" + this.parentDomId,
        stop: function( event, ui ) {              
          _self.onResize( 'top',  ui.helper[ 0 ].offsetTop );
          _self.onResize( 'left', ui.helper[ 0 ].offsetLeft );
          _self.show();
        }
      })
      .resizable({
        //containment: "#container", // <-- causes problems
        helper: "ui-resizable-helper",
    		start: function( e, ui ) {
          g_currentResizeId = ui.originalElement[0].id;
    		},
        resize: function( event, ui ) {                  
          _self.onResize( 'width',  ui.size.width );
          _self.onResize( 'height', ui.size.height );
          _self.show();
        },
    		stop: function( e, ui ) {  
          g_currentResizeId = '';
          _self.show();
    		}
      }).hover(
        function() { _self.highlight(  true ); },
        function() { _self.background( true ); }
      );
    
    var row = j( '<tr/>', {
        "class" : 'clsTblRow',
        name : this.domId,
        id : this.domId + 'tbl'
      });
    var td = j( '<td/>' );
    td.append( j( '<input/>', {
        name: 'name',
        type: 'text',
        id : this.domId + 'name',
        keyup: function(e) { e.preventDefault(); _self.onContentChanged( 'name', this.value ); },
        value : this.Name
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<input/>', {
        name: 'name',
        type: 'text',
        'id' : this.domId + 'left',    
        change: function(e) { e.preventDefault(); _self.onContentChanged( 'left', this.value ); this.value = _self.left; },
        "class" : "clsInpSize",
        'value' : _self.left
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'left', Number( j( '#' + _self.domId + 'left' ).attr( 'value' ) ) - g_frmConstraints.frmHorizontalGrid ); },
        "class" : 'btnLeft'
      }));
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'left', Number( j( '#' + _self.domId + 'left' ).attr( 'value' ) ) + g_frmConstraints.frmHorizontalGrid ); },
        "class" : 'btnRight'
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<input/>', {
        name: 'name',
        type: 'text',
        'id' : this.domId + 'top',
        change: function(e) { e.preventDefault(); _self.onContentChanged( 'top', this.value ); this.value = _self.top; },
        "class" : 'clsInpSize',
        'value' : this.top
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'top', Number( j( '#' + _self.domId + 'top' ).attr( 'value' ) ) - g_frmConstraints.frmVerticalGrid ); },
        "class" : 'btnUp'
      }));
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'top', Number( j( '#' + _self.domId + 'top' ).attr( 'value' ) ) + g_frmConstraints.frmVerticalGrid ); },
        "class" : 'btnDown'
      }));
    row.append( td ); 
    
    td = j( '<td/>' );
    td.append( j( '<input/>', {
        name: 'name',
        type: 'text',
        'id' : this.domId + 'width',
        change: function(e) { e.preventDefault(); _self.onContentChanged( 'width', this.value ); this.value = _self.width; },
        "class" : 'clsInpSize',
        'value' : this.width
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'width', Number( j( '#' + _self.domId + 'width' ).attr( 'value' ) ) - g_frmConstraints.frmHorizontalGrid ); },
        "class" : 'btnLeft'
      }));
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'width', Number( j( '#' + _self.domId + 'width' ).attr( 'value' ) ) + g_frmConstraints.frmHorizontalGrid ); },
        "class" : 'btnRight'
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<input/>', {
        name: 'name',
        type: 'text',
        'id' : this.domId + 'height',
        change: function(e) { e.preventDefault(); _self.onContentChanged( 'height', this.value ); this.value = _self.height; },
        "class" : 'clsInpSize',
        'value' : this.height
      }));
    row.append( td );
    
    td = j( '<td/>' );
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'height', Number( j( '#' + _self.domId + 'height' ).attr( 'value' ) ) - g_frmConstraints.frmVerticalGrid ); },
        "class" : 'btnUp'
      }));
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onContentChanged( 'height', Number( j( '#' + _self.domId + 'height' ).attr( 'value' ) ) + g_frmConstraints.frmVerticalGrid ); },
        "class" : 'btnDown'
      }));
    row.append( td ); 
    
    td = j( '<td/>' );
    td.append( j( '<div/>', {
        click: function(e) { e.preventDefault(); _self.onRemove( _self.domId ); },
        "class" : 'btnRemove'
      }));
    row.append( td );
    
    parent.addTableLine( row );
    
    j( '#' + this.domId + 'tbl' ).hover(
      function() { _self.highlight(); },
      function() { _self.background(); }
    );
    
    // boundary check
    this.changeValue( 'left',   this.left   );
    this.changeValue( 'top',    this.top    );
    this.changeValue( 'height', this.height );
    this.changeValue( 'width',  this.width  );
        
    this.show();
  };
  get() {
    return {
      Id      : this.Id,
      Name    : this.Name,
      width   : this.width,
      height  : this.height,
      left    : this.left,
      top     : this.top,
      isNew   : this.isNew,
      removed : this.removed,
      changed : this.changed
    };
  };
  onContentChanged( valueName, value ) {
    value = this.changeValue( valueName, value );
    this.show();
    return value;
  };
  onRemove( sId ) {
    this.parent.removeFrame( sId );
  };
  onResize( valueName, value ) {
    var pos = this.parent.getCorrection();
    var boShow = true;
    switch( valueName ) {
      case 'left':
        value = Number( value ) - pos.left;
        boShow = false;
        break;
      case 'top':
        value = Number( value ) - pos.top;
        boShow = false;
        break;
      default: break;
    }
    value = this.changeValue( valueName, value * g_scale );

    if( boShow ) {
      this.show();
    }
    return value;
  };
  highlight( boPointer ) {
    boPointer = boPointer || false;
    if( boPointer ) {
      j( '#' + this.domId ).css( 'cursor', 'pointer' );
    }
    j( '#' + this.domId ).fadeTo( 200, 1.0 );
    j( '#' + this.domId ).css( 'z-index', 100 );
    j( '#' + this.domId + 'tbl' ).fadeTo( 200, 1.0 );
  };
  background( boPointer ) {
  
    if( g_currentResizeId == this.domId ) { return; }
  
    boPointer = boPointer || false;
    if( boPointer ) {
      j( '#' + this.domId ).css( 'cursor', 'auto' );
    }
    j( '#' + this.domId ).fadeTo( 50, 0.3 );
    j( '#' + this.domId ).css( 'z-index', 'auto' );
    j( '#' + this.domId + 'tbl' ).fadeTo( 50, 0.3 );
  };        
  show() {
    var ojPar = j( '#' + this.parentDomId );
    var oParPos = j( ojPar ).position();                                              
    var ojFrm = j( '#' + this.domId );
    j( ojFrm ).width( this.width / g_scale );
    j( ojFrm ).height( this.height / g_scale );
    j( ojFrm ).css({ left : oParPos.left + ( this.left / g_scale ), top : oParPos.top + ( this.top / g_scale ), margin : 0});
    j( '#' + this.domId + 'name' ).html( this.Name );
  };
  checkVerticalContraintsValue( value ) {
    return Number( Math.round( Number( value ) / g_frmConstraints.frmVerticalGrid ) * g_frmConstraints.frmVerticalGrid );
  };
  checkHorizontalContraintsValue( value ) {
    return Number( Math.round( Number( value ) / g_frmConstraints.frmHorizontalGrid ) * g_frmConstraints.frmHorizontalGrid );
  };
  changeValue( valueName, value ) {
      this.changed = true;
    if( value < 0 ) {
      value = 0;
    }
    value = this.parent.boundaryCheck( this, valueName, value );
    switch( valueName ) {
    case 'name': this.Name = value;
      break;
    case 'left':
      value = this.checkHorizontalContraintsValue( value );
      this.left = value;
      break;
    case 'top':
      value = this.checkVerticalContraintsValue( value );
      this.top = value; 
      break;
    case 'width':
      value = Math.max( g_frmConstraints.frmMinWidth, this.checkHorizontalContraintsValue( value ) );
      this.width = value;
      break;
    case 'height':
      value = Math.max( g_frmConstraints.frmMinHeight, this.checkVerticalContraintsValue( value ) );
      this.height = value;
      break;
    default:
      break;
    }
    
    this.parent.collisionCheck();
    // set changed value to table;
    j( '#' + this.domId + valueName ).attr( 'value', value );
    
    return value;
  };
};
  
class CDisplay {
  constructor( idDispContainer, data ) {
    this.Id     = data.Id;
    this.Name   = data.Name;
    this.domId  = idDispContainer;
    this.frameContainerDomId  = idDispContainer + 'frm'; 
    this.tableContainerDomId  = idDispContainer + 'tab'; 
    this.width  = data.width  ;
    this.height = data.height ;
    this.lstFrames = new Array();
    var _self = this;
    this.isNew = data.isNew || false;
    this.removed = data.removed || false;
    this.changed = data.changed || false;
    this.default = data.default || false;
                                  
    j( '#' + this.domId ).append( '<div id="' + this.frameContainerDomId + 'Wrp" class="clsFrmContainerWrp"><div id="' + this.frameContainerDomId + '" class="clsFrmContainer"></div></div>' );
    
    var div = j( '<div/>', {
      id :  this.tableContainerDomId,
      'class' : 'clsTableContainer'
    });
    var table = j( '<table>' +
          '<tr><th>Name</th>' +
          '<th colspan="2">Left</th>' +
          '<th colspan="2">Top</th>' +
          '<th colspan="2">Width</th>' +
          '<th colspan="2">Height</th></tr>' +
        '</table><button id="' + this.domId + 'btnNewFrame" type="button">+</button>' );
    div.append( table );
    j( '#' + this.domId ).append( div );
    
    j( '#' + this.domId + 'btnNewFrame' ).on( 'click', function(){console.log( 'add  new frame' ); _self.addNewFrame();} );
                                                  
    var ojFrmContainer = j( '#' + this.frameContainerDomId );
    j( ojFrmContainer ).width( this.width / g_scale );
    j( ojFrmContainer ).height( this.height / g_scale );
                                                  
    ojFrmContainer = j( '#' + this.frameContainerDomId + 'Wrp' );
    j( ojFrmContainer ).width( this.width / g_scale );
    j( ojFrmContainer ).height( this.height / g_scale );

    if( data.frames ) {    
      for( var i=0; i<data.frames.length; i++ ) {
        this.addFrame( data.frames[ i ] );
      }
    } else if( data.lstFrames ) {    
      for( var i=0; i<data.lstFrames.length; i++ ) {
        this.addFrame( data.lstFrames[ i ] );
      }
    }
  };
  get() {
    var data = { Id : this.Id, Name : this.Name, isNew : this.isNew, removed : this.removed, changed : this.changed };
    data.frames = new Array();
    for( var i=0; i<this.lstFrames.length; i++ ) {
      data.frames[ i ] = this.lstFrames[ i ].get();
      data.frames[ i ].configId = i + 10;
    }
    data.default = this.default;
    return data; 
  };
  getCorrection() { 
    var elmnt = j( '#' + this.frameContainerDomId )[ 0 ];
    return { left: elmnt.offsetLeft, top:  elmnt.offsetTop };
  };
  show() {
    for( var i=0; i<this.lstFrames.length; i++ ) {
      if( !this.removed ) {
        
      }
      this.lstFrames[ i ].show();
    }
  };
  addTableLine( sLineContent ) {
    j( '#' + this.tableContainerDomId + ' > table' ).append( sLineContent );
  };
  addNewFrame() {
    this.addFrame({
    Id :g_tmpId,
    Name: 'new Frame',
    width: 256,
    height: 128,
    left: 0,
    top: 0,
    isNew:true
    });
    g_tmpId ++ ;
  };
  removeFrame( sId ) {
    var boRemoved = false;
    for( var i=0; i<this.lstFrames.length; i++ ) {
      if( this.lstFrames[ i ].domId == sId ) {
        console.log( 'remove frame (dom id' + sId + ')' );
        
        if( this.lstFrames[ i ].isNew ) {
            console.log( 'remove frame ' + this.lstFrames[ i ].Id + ' finally' );
            this.lstFrames.splice( i, 1 );
        } else {
            this.lstFrames[ i ].removed = true;
            console.log( 'mark frame ' + i + ' as removed' );
        }
        
        //this.lstFrames.splice( i, 1 );
        boRemoved = true;
        j( 'div' ).remove( '#' + sId );
        j( 'tr' ).remove( '#' + sId + 'tbl' );
        break;
      }
    }
    if( boRemoved ) {
        console.log( 'show again' );
      this.show();
    }
  };
  addFrame( data ) {
    data.isNew = data.isNew || false;
    this.lstFrames.push( new CFrame( this.frameContainerDomId, data, this, this.lstFrames.length ) );
  };
  collisionCheck() {
    for( var i=0; i<this.lstFrames.length; i++ ) {
      this.lstFrames[ i ].boCol = false;
    }
    for( var i=0; i<this.lstFrames.length; i++ ) {
      for( var j=0; j<this.lstFrames.length; j++ ) {
        if( i != j ) {
          var xi1 = this.lstFrames[ i ].left;
          var xi2 = this.lstFrames[ i ].width  + xi1 - 1;
          var yi1 = this.lstFrames[ i ].top;
          var yi2 = this.lstFrames[ i ].height + yi1 - 1;
          var xj1 = this.lstFrames[ j ].left;
          var xj2 = this.lstFrames[ j ].width  + xj1 - 1;
          var yj1 = this.lstFrames[ j ].top;
          var yj2 = this.lstFrames[ j ].height + yj1 - 1;
          
          if( ( ( xj1 >= xi1 ) && ( xj1 <= xi2 ) && ( yj1 >= yi1 ) && ( yj1 <= yi2 ) ) ||
              ( ( xj2 >= xi1 ) && ( xj2 <= xi2 ) && ( yj1 >= yi1 ) && ( yj1 <= yi2 ) ) ) /*||
              
              ( ( xj1 >= xi1 ) && ( xj1 <= xi2 ) && ( yj2 >= yi1 ) && ( yj2 <= yi2 ) ) ||
              ( ( xj2 >= xi1 ) && ( xj2 <= xi2 ) && ( yj2 >= yi1 ) && ( yj2 <= yi2 ) ) ) */{
              console.log( 'collision' );
              this.lstFrames[ i ].boCol = true;
              this.lstFrames[ j ].boCol = true;
          } 
        }
      }
    }
  };
  remove() {
    this.removed = true;
    this.lstFrames = new Array();  
  };
  boundaryCheck( frm, valueName, value ) {
    if( frm ) {
      switch( valueName ) {
      case 'left':
        value = Math.min( value, this.width - frm.width );
        break;
      case 'top':
        value = Math.min( value, this.height - frm.height );
        break;
      case 'width':
        value = Math.min( value, this.width - frm.left );
        break;
      case 'height':
        value = Math.min( value, this.height - frm.top );
        break;
      default: break;
      }
      return value;
    }
    return 0;
  };
};
  
class CDevice {
  constructor( idDevContainer, data, boServer ) {
    this.Id   = data.Id; 
    this.Name = data.Name;
    this.lstDisplays = new Array();
    this.idDevContainer = idDevContainer;
    this.data = data;
    this.boServer = boServer || false;
    this.resX = 0;
    this.resY = 0;
    if( this.boServer ) {
        this.resX = data.resX;
        this.resY = data.resY;
    } else {
    }
    
    this.rebuild( cloneObj( data.displays ), true );
    
    return;
  };
  
  rebuild( lstDisplays, boInitial ) {

    var parentDiv = j( '#' + this.idDevContainer );
    this.lstDisplays = new Array();
    
    var sContent = '';
    var _self = this;
    var sOnRemove = '';
    boInitial = boInitial || false;
    
    parentDiv.html('');
    parentDiv.empty();
    parentDiv.append( '<ul id="dev' + this.Id + 'lst"></ul>' );
    parentDiv.data( 'dev', this );
    
    if( this.boServer ) {
      console.log( 'server option active' );
      var li = j( '<li/>' );
      li.append( j( '<button/>', {
        type : 'button',
        text: '+',
        click: function() {
            var sTmpDispId = 'dev' + _self.Id + 'disp' + g_tmpId;
            var newData = { Name : 'new frameset',
                Id : g_tmpId,
                width : _self.resX,
                height : _self.resY,
                isNew : true
            };
            _self.addDisplay( sTmpDispId, newData );
            g_tmpId ++ ;
            _self.rebuild( cloneObj( _self.lstDisplays ) );
        }
      }));
      j( '#dev' + this.Id + 'lst' ).append( li );
      parentDiv.append( '<div/>' );
    }
    
    for( var i=0; i<lstDisplays.length; i++ ) {
      var sDispId = 'dev' + this.Id + 'disp' + lstDisplays[ i ].Id;
      if( !lstDisplays[ i ].removed ) {
        var li = j( '<li/>' );
        if( this.boServer ) {
          var lnk = j( '<a/>', {
            href : '#' + sDispId
          });
          var inp = j( '<input></input>', {
            type : 'text',
            size:'10',
            draggable: false,
            droppable: false,
            value: lstDisplays[ i ].Name,
            id: 'ren' + lstDisplays[ i ].Id,
            keyup: function() { _self.changeFramesetName( this.id, this.value ); }
          });
          lnk.append( inp );
        } else {
          var lnk = j( '<a/>', {
            href : '#' + sDispId,
            text: lstDisplays[ i ].Name
          });
        }
        li.append( lnk );
        if( this.boServer ) {
          li.append( j( '<button/>', {
            type : 'button',
            text: 'X',
            id: 'rm' + lstDisplays[ i ].Id,
            click: function() { _self.remove( this.id ); }
          }));
          var radDefault = j( '<input/>', {
            type : 'radio',
            text: 'default:',
            name: 'radDefault' + _self.Id,
            title: g_langSetToDefault,
            value: lstDisplays[ i ].Id,
            id: 'def' + lstDisplays[ i ].Id,
            click: function() { _self.setDefault( this.id ); }
          });
          if( lstDisplays[ i ].default || ( lstDisplays.length == 1 ) ) {
            lstDisplays[ i ].default = true;
            j( radDefault ).prop( 'checked', true );
          }
          li.append( radDefault );
        }
        j( '#dev' + this.Id + 'lst' ).append( li );
        parentDiv.append( '<div id="' + sDispId + '">' );
      }
      this.addDisplay( sDispId, lstDisplays[ i ] );
    }
    
    if( !boInitial ) parentDiv.tabs( 'destroy' );
    parentDiv.tabs({
      activate: function( event, ui ) {
        _self.showTab( ui.newPanel.selector.slice( 1 ) );
      },
      draggable: false,
      droppable: false
    });
    this.show();
  };
  
  setDefault( sId ) {
    var iId = Number( sId.slice(3) );
    for( var i=0; i<this.lstDisplays.length; i++ ) {
      if( this.lstDisplays[ i ].Id == iId ) {
        this.lstDisplays[ i ].default = true;
      } else {
        this.lstDisplays[ i ].default = false;
      }
    }
  };
  
  changeFramesetName( idFrameset, sNewName ) {
    if( sNewName !== undefined ) {
      var iIdx = Number( idFrameset.slice(3) );
      for( var i=0; i<this.lstDisplays.length; i++ ) {
        if( this.lstDisplays[ i ].Id == iIdx ) {
          this.lstDisplays[ i ].Name = sNewName;
          this.lstDisplays[ i ].changed = true;
        }
      }
    }
  };
  
  get() {
    var data = { Id : this.Id, Name : this.Name };
    data.displays = new Array();
    for( var i=0; i<this.lstDisplays.length; i++ ) {
      data.displays[ i ] = this.lstDisplays[ i ].get();
    }
    return data; 
  };
  
  remove( idx ) {
    for( var i=0; i<this.lstDisplays.length; i++ ) {
      var iIdx = Number( idx.slice(2) );
      if( this.lstDisplays[ i ].Id == iIdx ) {
        if( this.lstDisplays[ i ].isNew ) {
            this.lstDisplays.splice( i, 1 );
            console.log( 'remove frameset ' + iIdx + ' finally' );
        } else {
            this.lstDisplays[ i ].remove(); //removed = true;
            console.log( 'mark frameset ' + iIdx + ' as removed' );
        }
        break;
      }
    }
    this.rebuild( cloneObj( this.lstDisplays ) );
  };
  showInitial() {
    this.rebuild( cloneObj( this.lstDisplays ) );
  };
  show() {
    if( this.lstDisplays ) {
      for( var i=0; i<this.lstDisplays.length; i++ ) {
        if( !this.lstDisplays[ i ].removed ) {
          this.lstDisplays[ i ].show();
        }
      }
    }
  };
  showTab( sId ) {
    if( sId && this.lstDisplays ) {
      for( var i=0; i<this.lstDisplays.length; i++ ) {
        if( this.lstDisplays[ i ].domId == sId ) {
          this.lstDisplays[ i ].show();
        }
      }
    }
  };
  addDisplay( sDispId, data ) {
    this.lstDisplays.push( new CDisplay( sDispId, data ) );
  };
};