// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

/**
 * Coordinate conversions!
 */
ExtApp.topWindowGroup = new Ext.WindowGroup();
ExtApp.topWindowGroup.zseed = 21000;

Number.prototype.toDMS = function(dp) {  // convert numeric degrees to deg/min/sec
  if (arguments.length < 1) { dp = 0; }      // if no decimal places argument, round to int seconds
  var d = Math.abs(this);  // (unsigned result ready for appending compass dir'n)
  var deg = Math.floor(d);
  var min = Math.floor((d-deg)*60);
  var sec = ((d-deg-min/60)*3600).toFixed(dp);
  // fix any nonsensical rounding-up
  if (sec==60) { sec = (0).toFixed(dp); min++; }
  if (min==60) { min = 0; deg++; }
  if (deg == 360) { deg = 0; }
  // add leading zeros if required
  if (deg < 100) {
    deg = '0' + deg;
    if (deg < 10) { deg = '0' + deg; }
  }
  if (min<10) { min = '0' + min; }
  if (sec<10) { sec = '0' + sec; }
  return deg + '\u00B0' + min + '\u2032' + sec + '\u2033';
};

Number.prototype.toUTMLat = function(lat)
{
  if (lat >= 85) { lat = 85; }
  if (lat <= -85) { lat = -85; }
  return Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI) * 20037508.34;
};

Number.prototype.toUTMLng = function(lng)
{
  return lon * 20037508.34 / 180;
};

Number.prototype.toLat = function(dp) {  // convert numeric degrees to deg/min/sec latitude
  return this.toDMS(dp).slice(1) + (this<0 ? 'S' : 'N');  // knock off initial '0' for lat!
};

Number.prototype.toLon = function(dp) {  // convert numeric degrees to deg/min/sec longitude
  return this.toDMS(dp) + (this>0 ? 'E' : 'W');
};

Ext.ux.ActionPlugin = function(config) {
  this.actions = {};
  this.tbar = [];
  
  Ext.apply(this, config, {
    /**
     * @config cbar NOTE: This only works with grid panels! Create a context menu for a grid row, if an action with the
     * name given exits it will be added to to the menu otherwise it is added unchanged to the menu.
     */
    cbar: false,

    /**
     * @config tbar Creates a top toolbar for the panel, if an action with the name given exits it will be added to
     * to the menu otherwise it is added unchanged to the menu.
     */
    tbar: false,

    /**
     * @config bbar Creates a bottom toolbar for the panel, if an action with the name given exits it will be added to
     * to the menu otherwise it is added unchanged to the menu.
     */
    bbar: false,

    init: function(panel) {
      this.setup_actions(panel);
      this.setup_menus(panel);
    },

    setup_actions: function(panel) {
      panel.actions = {};

      for(var index in this.actions) {
        if(!this.actions[index].isAction && this.actions[index].text) {
          Ext.applyIf(this.actions[index], { scope: panel });
          panel.actions[index] = new Ext.Action(this.actions[index]);
        } else {
          panel.actions[index] = this.actions[index];
        }
      }
    },

    setup_menus: function(panel) {
      this.add_menu_items(panel.actions, this.tbar, panel.getTopToolbar());
      this.add_menu_items(panel.actions, this.bbar, panel.getBottomToolbar());
      var ctxMenu = this.add_menu_items(panel.actions, this.cbar, new Ext.menu.Menu());
      if(ctxMenu) {
        panel.on('rowcontextmenu', function(grid, index, e) {
          e.stopEvent();
          var sm = grid.getSelectionModel();
          if (sm.getSelected() != grid.getStore().getAt(index)) {
            grid.getSelectionModel().selectRow(index);
          }
          ctxMenu.showAt(e.getXY());
        });
      }
    },

    add_menu_items: function(actions, config, toolbar) {
      if(!config || !toolbar) { return null; }

      Ext.each(config, function(item) {
        this.add_menu_item(toolbar, actions, item);
      }, this);

      return toolbar;
    },

    add_menu_item: function(toolbar, actions, item) {
      if(!item) { return; }
      if(typeof item == 'string' && actions[item]) {
        toolbar.add(actions[item]);
      } else {
        toolbar.add(item);
      }
    }
  });

  return this;
}

Ext.ux.ActionPanel = Ext.extend(Ext.Panel, {
  actions: {},
  top_row_menu: [],

  initComponent: function() {
    this.setupActions();
    this.setupMenuBars();

    Ext.ux.ActionPanel.superclass.initComponent.call(this);
  },

  setupActions: function() {
    for(var index in this.actions) {
      if (!this.actions[index].isAction) {
        if(this.actions[index].text) {
          Ext.applyIf(this.actions[index], { scope: this });
          this.actions[index] = new Ext.Action(this.actions[index]);
        }
      }
    }
  },

  setupMenuBars: function() {
    if(this.tbar === false) { return; }
    if (!this.tbar) { this.tbar = new Ext.Toolbar(); }

    Ext.each(this.top_row_menu, function(item) {
      if(typeof item == 'string' && this.actions[item]) {
        this.tbar.add(this.actions[item]);
      } else {
        this.tbar.add(item);
      }
    }, this);
  }
});

Ext.ns('Ext.ux.Asset');

Ext.ux.Asset.Revisions = Ext.extend(Ext.grid.GridPanel, {
  initComponent: function() {
    this.store = new Ext.data.JsonStore({
      fields: [
        'id', 'name', 'asset_id', 'description',
        { name: 'created_at', type: 'date' },
        { name: 'updated_at', type: 'date' },
        { name: 'finalized_at', type: 'date' },
        { name:'archived_at', type: 'date' }
      ],
      sortInfo: {
        field: 'updated_at',
        direction: 'DESC'
      },
      filterArchived: function() {
        this.filterBy(function(record) {
          if(this.show_archived) {
            return true;
          } else {
            return record.get('archived_at') == '';
          }
        }, this);
      },
      root: 'revisions',
      url: this.editor.url + '/revisions',
      data: this.data,
      restful: true,
      writer: new Ext.data.JsonWriter({ encode: false })
    });
    this.store.filterArchived();
    
    this.store.on('exception', function(proxy, type, action, options, response, record) {
      if(type == 'remote') {
        message = response.flash || "An error occured while saving";
        ExtApp.Notice.show({ msg: message });
        record.data = response.revisions[0];
        record.commit();
      }
    });
    
    this.store.on('load', function() {
      this.getStore().filterArchived();
      Ext.getCmp(this.filegrid).getStore().load();
    }, this);

    this.editor.on('urlchange', function(editor, url) {
      this.store.proxy.conn.url = url + '/revisions';
    }, this);

    Ext.apply(this, {
      loadMask: true,
      tbar: new Ext.Toolbar(),
      viewConfig: { forceFit: true },
      sm: new Ext.grid.RowSelectionModel({
        listeners: {
          scope: this,
          rowdeselect: function() {
            this.ctxRecord = null;
            Ext.getCmp(this.filegrid).ctxRecord = null;
            Ext.getCmp(this.filegrid).disable();
          },
          rowselect: function(sm, index, record) {
            this.ctxRecord = record;
            Ext.getCmp(this.filegrid).ctxRecord = record;
            var grid = Ext.getCmp(this.filegrid);
            grid.getStore().filter('asset_revision_id', record.get('id'));
            grid.enable();
          }
        }
      }),
      autoExpandColumn: 'description',
      columns: [{
        header: 'ID',
        hidden: true,
        dataIndex: 'id',
        sortable: true,
        width: 30
      }, {
        header: 'Name',
        dataIndex: 'name',
        sortable: true,
        width: 100
      }, {
        id: 'description',
        header: 'Description',
        sortable: true,
        dataIndex: 'description',
        width: 200
      }, {
        header: 'Last Updated',
        dataIndex: 'updated_at',
        sortable: true,
        renderer: Ext.util.Format.dateRenderer('Y-m-d'),
        width: 100
      }, {
        header: 'Archived',
        dataIndex: 'archived_at',
        sortable: true,
        renderer: function(value) {
          if(value) {
            return Ext.util.Format.dateRenderer('Y-m-d')(value);
          } else {
            return 'No';
          }
        },
        width: 100
      }]
    });

    Ext.ux.Asset.Revisions.superclass.initComponent.call(this);
  },

  edit: function(record) {
    var win = new Ext.Window({
      title: 'Asset Revision',
      width: 500,
      items: {
        itemId: 'form',
        border: false,
        bodyStyle: 'padding: 3px 3px 0 0;',
        xtype: 'form',
        defaults: { anchor: '100%' },
        items: [{
          xtype: 'textfield',
          name: 'name',
          fieldLabel: 'Name'
        }, {
          xtype: 'textfield',
          name: 'description',
          fieldLabel: 'Description'
        }]
      },
      buttons: [
        { text: 'Cancel', scope: this, handler: function() { win.close(); } },
        { text: 'Submit', scope: this, handler: function() {
          var store = this.getStore();
          if(!record) {
            record = new (this.getStore().recordType)();
          }
          win.get('form').getForm().updateRecord(record);
          if(!record.store) {
            this.getStore().addSorted(record);
          }

          win.close();
        } }
      ]
    });

    win.show();
    if(record) {
      win.get('form').getForm().loadRecord(record);
    }
  }
});
Ext.reg('assetrevisions', Ext.ux.Asset.Revisions);

Ext.ux.Asset.Files = Ext.extend(Ext.grid.GridPanel, {
  initComponent: function() {
    this.store = new Ext.data.JsonStore({
      fields: [
        'id', 'filename', 'asset_revision_id', 'mimetype', 'size', {
        name: 'updated_at', type: 'date'
      }],
      url: this.editor.url,
      data: this.data
    });

    this.editor.on('urlchange', function(editor, url) {
      this.store.url = url + '/files';
    }, this);

    Ext.apply(this, {
      tbar: new Ext.Toolbar(),
      maskDisabled: false,
      autoExpandColumn: 'filename',
      columns: [{
        header: 'ID',
        dataIndex: 'id',
        hidden: true,
        sortable: true,
        width: 30
      },{
        id: 'filename',
        header: 'Filename',
        dataIndex: 'filename',
        sortable: true,
        width: 100
      },{
        header: 'Mime Type',
        dataIndex: 'mimetype',
        sortable: true,
        width: 150
      },{
        header: 'Size',
        dataIndex: 'size',
        sortable: true,
        renderer: Ext.util.Format.fileSize,
        width: 100
      },{
        header: 'Last Updated',
        dataIndex: 'updated_at',
        sortable: true,
        renderer: Ext.util.Format.dateRenderer('Y-m-d h:i A'),
        width: 150
      }]
    });

    Ext.ux.Asset.Files.superclass.initComponent.call(this);

    /**
     * Handle the disable masking ourselves so we can show help text to the user
     */
    this.on('disable', function(p) {
      this.getStore().filterBy(function() { return false; });
      p.getEl().mask('<b>Click on a row from the "Revisions" list in order to view the asset files.<br />' +
                     'If no revision exists, one must be created before files can be uploaded.</b>', 'asset-file-instructions-mask');
    }, this);
    this.on('enable', function(p) {
      p.getEl().unmask();
    });
    this.on('afterrender', function(p) {
      /* Disable after short delay so the message centers properly */
      p.disable.defer(50, p);
    }, this, { single: true });

    this.store.on('load', function() {
      if(this.ctxRecord) {
        this.getStore().filter('asset_revision_id', this.ctxRecord.get('id'));
      } else {
        this.getStore().filterBy(function() { return false; });
      }
    }, this)
  }
});
Ext.reg('assetfiles', Ext.ux.Asset.Files)

Ext.ux.LocationFieldEditor = Ext.extend(Ext.form.TriggerField, {
  grid: null,
  
  initComponent: function() {
    this.addEvents('updated');

    Ext.ux.LocationFieldEditor.superclass.initComponent.call(this, new Ext.form.TextField);
  },
  
  onTriggerClick: function(e) {
    this.showEditor(e.record);
  },

  /**
   * Fires off when the value for the editor needs to be updated
   * @param aoi {GMarker|GPolygon}
   */
  onUpdate: function(aoi) {
    var coords = '';
    if(aoi.getVertex) {
      var points = [];
      for(var ii=0; ii < aoi.getVertexCount(); ii++) {
        var v = aoi.getVertex(ii);
        points.push(v.lng() + ' ' + v.lat())
      }
      
      coords = points.join(',');
    } else if(aoi.lng && aoi.lat) {
      coords = aoi.lng() + ' ' + aoi.lat()
    }
    
    this.setValue(coords);

    this.fireEvent('updated', this, aoi, coords)
  },

  showEditor: function(record) {
    if(this.grid.ctxRecord.get('type') == 'POINT') {
      this.showMarker();
    } else {
      this.showPolygon();
    }
  },

  showPolygon: function() {
    var coords,
        points = [],
        polygon,
        value = this.getValue();

    this.setupMapWindow();

    Ext.each(value.split(','), function(coords) {
      coords = coords.split(' ');
      points.push(new GLatLng(coords[1], coords[0]));
    });

    polygon = new GPolygon(points);
    if(!this.map.isGMapReady) {
      this.map.on('gmaploaded', function(map) {
        this.renderPolygon(polygon);
      }, this);
    } else {
      this.renderPolygon(polygon);
    }

    GEvent.addListener(polygon, "click", function(latlng, index) {
      if (typeof index == "number") {
        this.deleteVertex(index);
      }
    });
    GEvent.addListener(polygon, 'lineupdated', function() {
      this.onUpdate.call(this, polygon);
    }.createDelegate(this));

    this.mapWindow.show();
  },

  renderPolygon: function(polygon) {
    var zoom = this.map.getMap().getBoundsZoomLevel(polygon.getBounds()) - 1;
    this.loadOverlay(polygon, zoom);
    polygon.enableEditing();
  },

  showMarker: function() {
    var coords, point, marker, zoom;
    var value = this.getValue();

    this.setupMapWindow();

    if(value) {
      coords = value.split(' ');
      point = new GLatLng(coords[1], coords[0]);
      zoom = 14;
    } else {
      point = new GLatLng(64.856, -147.849);
      zoom = 4;
    }
    marker = new GMarker(point, { draggable: true, bouncy: true });
    GEvent.bind(marker, 'dragend', this, function(latlng) {
      this.onUpdate.call(this, latlng);
    });

    if(!this.map.isGMapReady) {
      this.map.on('gmaploaded', function(map) {
        this.loadOverlay(marker, zoom);
      }, this);
    } else {
      this.loadOverlay(marker, zoom);
    }
    this.mapWindow.show();
  },

  setupMapWindow: function() {
    if(!this.map) {
      this.map = new Ext.ux.GMapPanel();
    }
    if(!this.mapWindow) {
      this.mapWindow = new Ext.Window({
        title: 'Location Markers',
        closable: false,
        modal: true,
        width: 900,
        height: 600,
        layout: 'fit',
        manager: ExtApp.topWindowGroup,
        buttons: [{
          text: 'Close',
          scope: this,
          handler: function() { this.stop(); }
        }],
        items: [this.map],
        listeners: {
          scope: this,
          close: function() {
            this.grid.stopEditing(false);
            this.mapWindow = null;
            this.map = null;
          }
        }
      });
    }
  },
  stop: function() {
    this.mapWindow.close();
  },

  loadOverlay: function(overlay, zoom) {
    this.map.clearOverlays();
    this.map.addOverlay(overlay);
    if(overlay.getLatLng) {
      this.map.goto(overlay.getLatLng(), zoom);
    } else {
      this.map.goto(overlay.getBounds().getCenter(), zoom)
    }
  },

  validateBlur: function(e) {
    //Don't allow blur if the window is visible
    if(!this.mapWindow || this.mapWindow.hidden) {
      return true;
    } else {
      return false;
    }
  }
});
Ext.reg('locationfieldeditor', Ext.ux.LocationFieldEditor);


Ext.ux.form.GcmdThemeSelector = Ext.extend(Ext.ux.form.SuperBoxSelect, {
  initComponent: function() {
    Ext.apply(this, {
      fieldLabel: 'GCMD Keywords',
      emptyText: 'Search for GCMD Keywords by typing here',
      resizable: true,
      minChars: 2,
      name: this.prefix + '[gcmd_themes][]',
      store: new Ext.data.JsonStore({
        fields: ['id', 'name', 'path'],
        url: '/gcmd_themes.json',
        restful: true
      }),
      mode: 'remote',
      displayField: 'path',
      displayFieldTpl: '{path}',
      valueField: 'id',
      queryDelay: 0,
      triggerAction: 'all'
    });

    Ext.ux.form.GcmdThemeSelector.superclass.initComponent.call(this);
  }
});

Ext.ux.form.TagSelector = Ext.extend(Ext.ux.form.SuperBoxSelect, {
  initComponent: function() {
    Ext.apply(this, {
      allowAddNewData: true,
      fieldLabel: 'Tags',
      resizable: true,
      minChars: 2,
      name: this.prefix + '[tags][]',
      emptyText: 'Search for Tags by typing here',
      store: new Ext.data.JsonStore({
        fields: ['id', 'text'],
        data: this.data,
        sortInfo: {
          field: 'text',
          direction: 'ASC'
        }
      }),
      mode: 'local',
      displayField: 'text',
      displayFieldTpl: '{text}',
      valueField: 'text',
      triggerAction: 'all'
    });

    Ext.ux.form.TagSelector.superclass.initComponent.call(this);
    this.on('newitem', function(store, item) {
      var data = { id: item, text: item };
      store.addItem(data);
    });
  }
});

Ext.ux.EventManager = function(config) {
  config = config || { };
  this.initalConfig = config;

  this.controller = config.controller;
  this.pluginPanels = {};
  this.listeners = config.listeners || {}; 

  /* Install the default response listener */
  Ext.applyIf(this.listeners, {
    'response': function(response, record, action, panel) {
      var message = response.flash || '';
      if(response.errors && response.errors.size() > 0) {
        if(message == '') { message = 'An error occured while saving...'; }
        Ext.each(response.errors, function(item) {
          message += "<br />" + Ext.util.Format.capitalize(item[0] + ' ' + item[1]);
        }, this);
      }
      if(message) { ExtApp.Notice.show({ msg: message }); }
    }
  });

  this.navigation = function(request) {
    if(request.action) {
      this.fireEvent(request.action, this, request.params);
    }
  }.createDelegate(this);

  if(config.events) {
    this.addEvents(config.events);
  }
  if(config.debug && this.listeners) {
    for(var event in this.listeners) {
      this.on(event, function() {
        console.log('DEBUG: ', this.event, arguments);
      }, { event: event });
    }
  }
  Ext.ux.EventManager.superclass.constructor.call(this);
};
Ext.extend(Ext.ux.EventManager, Ext.util.Observable, {
  init: function(item) {
    item.em = this;
  }
});

ExtApp.em = new Ext.ux.EventManager({
  events: ['response']
});