var map;
var satMap;
var street;
var showBoth = false;
var earth;
var earthMap;
var isDebug = true;
var streetClient;
var menu = null;

var REFRESH_DISTANCE = 5000;
var MAP_DEFAULT_ZOOM_LEVEL = 12;

var urlInfo = null;
var geocoder = null;

var DEFAULT_RANGE = 1800000;
var DEFAULT_ALTITUDE = 48000;
var DEFAULT_TILT = 45;

var API_DOMAIN = 'http://api.tori.st';

function getSatListUrl(){
    return API_DOMAIN + "/api/sat/list.json?callback=satArray&jsonPrettyPrint=true";
}

var SAT_LABELS;

//Sat constructor
var Sat = function(obj){
    this.index = parseInt(obj.index);
    this.iconImageUrl = obj.iconImageUrl;
    this.positionLoaded = false;
    this.name = obj.name;
    this.recommendTwitterBot = null;
    this.recommendGoogleCalendar = null;
    this.code = obj.code;
    this.launchDate = (obj.launchDate) ? Util.parseDate(obj.launchDate) : null;
    this.photoUrl = obj.photoUrl;
    this.organization = obj.organization;
    this.urls = obj.urls || new Array();
    this.selected = false;
    this.polyline = null;
    this.color = Util.getColor();
    this.schedules = new Array();
};
Sat.prototype.setRecommendTwitterBot = function(id){
    if (this.recommendTwitterBot == null) {
        this.recommendTwitterBot = id;
        this.refreshRecommendTwitterBot();
    }
};
Sat.prototype.setRecommendGoogleCalendar = function(id){
    if (this.recommendGoogleCalendar == null) {
        this.recommendGoogleCalendar = id;
        this.refreshRecommendGoogleCalendar();
    }
};
Sat.prototype.refreshRecommendTwitterBot = function(){
    if (this.recommendTwitterBot) {
        if (!Element.visible($('panelTwitter'))) 
            Effect.SlideDown($('panelTwitter'));
        $('linkTwitter').href = 'http://twitter.com/' + this.recommendTwitterBot;
        $('labelTwitterBotname').innerHTML = this.recommendTwitterBot;
    }
};
Sat.prototype.refreshRecommendGoogleCalendar = function(){
    if (this.recommendGoogleCalendar) {
        if (!Element.visible($('panelGoogleCalendar'))) 
            Effect.SlideDown($('panelGoogleCalendar'));
        $('linkGoogleCalendar').href = "http://www.google.com/calendar/render?cid=" + encodeURI(this.recommendGoogleCalendar);
    }
}
Sat.prototype.resetRecommendTwitterBot = function(){
    this.recommendTwitterBot = null;
    this.recommendGoogleCalendar = null;
}
Sat.prototype.calcCurrentPosition = function(date){
    var positions = this.positions;
    if (!(positions && positions.length > 0)) 
        return;
    var tmpPosition = positions[0];
    var tmpIndex = 0;
    var setFlg = false;
    for (var i = 0, l = positions.length; i < l; i++) {
        var pos = positions[i];
        if (!Util.dateIsBefore(date, pos.time)) {
            tmpIndex = i;
            tmpPosition = pos;
            setFlg = true;
        }
        else {
            break;
        }
    }
    var position;
    var direction = null;
    if (tmpPosition && tmpIndex + 1 < positions.length) {
        var nextPosition = positions[tmpIndex + 1];
        var complationRatio = (date.getTime() - tmpPosition.time.getTime()) / (nextPosition.time.getTime() - tmpPosition.time.getTime());
        position = Util.getDivisionPoint(tmpPosition, nextPosition, complationRatio);
        position.time = date;
        direction = Util.getAzimuthByLatLngs(tmpPosition, nextPosition);
    }
    else {
        position = tmpPosition;
    }
    var direction = direction;
    this.currentPosition = {
        index: 0,
        posCount: positions.length,
        position: position,
        direction: direction
    };
}
Sat.prototype.getNearestPosition = function(date){
    if (this.currentPosition == null) 
        this.calcCurrentPosition(date);
    return this.currentPosition;
}
Sat.prototype.load = function(){
    printDebug("TODO loading sat no." + this.index);
};
Sat.prototype.getTip = function(){
    var div = document.createElement('div');
    div.className = (this.index % 2 == 0) ? 'tip odd' : 'tip even';
    var check = document.createElement('input');
    this.checkbox = check;
    check.type = 'radio';
    check.name = 'satSelect';
    var label = document.createElement('span');
    label.innerHTML = this.name;
    div.appendChild(check);
    div.appendChild(label);
    Event.observe(check, 'change', this.getSelectAction());
    return div;
}
Sat.prototype.getSatScheduleUrl = function(){
    var position = map.getCenter();
    return API_DOMAIN + "/api/sat/predict.json?" +
    "onlyNakedEyeOk=false&satCode=" +
    this.code +
    "&latitude=" +
    position.lat() +
    "&longitude=" +
    position.lng() +
    "&limit=10&callback=satSchedule&jsonPrettyPrint=true&printPlots=true&" +
    "year=" +
    urlInfo.getGmtYear() +
    "&month=" +
    urlInfo.getGmtMonth() +
    "&day=" +
    urlInfo.getGmtDay() +
    "&hour=" +
    urlInfo.getGmtHour() +
    "&min=" +
    urlInfo.getGmtMin() +
    '&timeZone=Etc/GMT';
};

Sat.prototype.getSatKmlUrl = function(){
    var position = map.getCenter();
    return "http://gae-api.tori.st/api/sat/position.kml?" +
    "satCode=" +
    this.code +
    "&latitude=" +
    position.lat() +
    "&longitude=" +
    position.lng() +
    "&year=" +
    urlInfo.getYear() +
    "&month=" +
    urlInfo.getMonth() +
    "&day=" +
    urlInfo.getDay() +
    "&hour=" +
    urlInfo.getHour() +
    "&min=" +
    urlInfo.getMin();
};

var SELECT_FIRST_SCHEDULE_AUTO = true;
var firstScheduleSelected = false;
Sat.prototype.setSchedules = function(obj){
    //clear
    this.clear();
    if (obj.basic.recommendTwitterBot) {
        this.setRecommendTwitterBot(obj.basic.recommendTwitterBot);
    }
    if (obj.basic.icalUrl) {
        this.setRecommendGoogleCalendar(obj.basic.icalUrl);
    }
    for (var i = 0, l = obj.schedules.length; i < l; i++) {
        var schedule = new SatSchedule(obj.schedules[i], this);
        this.schedules.push(schedule);
    }
    
    if (obj.schedules.length == 0) {
        Message.showMessage(Message.SAT_SCHEDULE_EMPTY, [this.name, Util.formatDate(urlInfo.targetDate, 'MM/DD HH:mm')]);
    }
    else {
        Message.showMessage(Message.SAT_SCHEDULE_FOUND, [this.name, Util.formatDate(urlInfo.targetDate, 'MM/DD HH:mm')]);
        this.schedules[0].showMessage();
    }
    this.selectBestSchedule();
}
Sat.prototype.selectBestSchedule = function(){
    if (this.schedules.length == 0) 
        return;
    
    var bestScheduleIndex = 0;
    for (var i = 0, l = this.schedules.length; i < l; i++) {
        if (this.schedules[i].nakedEyeOk) {
            bestScheduleIndex = i;
            break;
        }
    }
    this.schedules[bestScheduleIndex].select();
};

Sat.prototype.getSelectAction = function(){
    var self = this;
    return function(){
        self.select();
    };
};
Sat.prototype.select = function(){
    this.iconUrl = this.iconImageUrl || Sat.DEFAULT_ICON_URL;
    wSatMap.setSat(this);
    wWorldMap.setSat(this);
    $('satIconImg').src = this.iconUrl;
    Player.hide();
    this.showDetail();
    firstScheduleSelected = false;
    var position = map.getCenter();
    this.selected = this.checkbox.checked;
    Sat.clearTabs();
    Util.loadJsonp(this.getCourseUrl());
    //Util.loadJsonp(this.getCalendarUrl());
    SatMap.selectedSat = this;
    this._displayName();
    urlInfo.refresh(true);
    $('linkKml').style.display = 'inline';
    $('linkKml').href = this.getSatKmlUrl();
}
Sat.prototype._displayName = function(){
    for (var i = 0, l = SAT_LABELS.length; i < l; i++) {
        SAT_LABELS[i].innerHTML = this.name;
    }
};
Sat.prototype.clear = function(){
    this.selectedSchedule = null;
    Sat.clearTabs();
    this.schedules = new Array();
    this.resetRecommendTwitterBot();
}
Sat.clearTabs = function(){
    var container = $('scheduleTabContainer');
    var removeTabs = new Array();
    for (var i = 0, l = container.childNodes.length; i < l; i++) {
        if ($('prevTab') != container.childNodes[i] && $('nextTab') != container.childNodes[i]) 
            removeTabs.push(container.childNodes[i]);
    }
    for (var i = 0, l = removeTabs.length; i < l; i++) 
        container.removeChild(removeTabs[i]);
};
Sat.prototype.getCourseUrl = function(){

    var position = map.getCenter();
    var longitude = position.lng();
    var latitude = position.lat();
    return API_DOMAIN + "/api/sat/position.json?smooth=true&satCode=" +
    this.code +
    "&callback=renderCourse&jsonPrettyPrint=true" +
    "&latitude=" +
    latitude +
    "&longitude=" +
    longitude +
    '&timeZone=Etc/GMT';
    
};
Sat.prototype.getCalendarUrl = function(){
    var position = map.getCenter();
    var longitude = position.lng();
    var latitude = position.lat();
    return API_DOMAIN + "/api/sat/fixed/predict.json?satCode=" +
    this.code +
    "&callback=renderCalendar&jsonPrettyPrint=true" +
    "&latitude=" +
    latitude +
    "&longitude=" +
    longitude +
    '&timeZone=Etc/GMT';
};
Sat.prototype.renderCalendar = function(obj){
    this.calendar = new SatCalendar(this, obj);
};
Sat.prototype.setPolyline = function(obj){
    this.positions = obj.positions;
    for (var i = 0, l = this.positions.length; i < l; i++) {
        var pos = this.positions[i];
        pos.time = Util.parseDateAsGmt(pos.time);
    }
    var positions = obj.positions;
    var latLngs = new Array();
    for (var i = 0, l = positions.length; i < l; i++) {
        var position = positions[i];
        var longitude = parseFloat(position.longitude);
        var latitude = parseFloat(position.latitude);
        latLngs.push(new GLatLng(latitude, longitude));
    }
    
    this.polyline = new GPolyline(latLngs, this.color, 1);
    wWorldMap.drawOrbit(this.polyline);
    wWorldMap.move();
    this.positionLoaded = true;
}
Sat.prototype.seePeek = function(){
    scheduleIndex = Math.floor(this.getSelectedSchedule().positions.length / 2);
    this.see(scheduleIndex);
}
Sat.prototype.getSelectedSchedule = function(){
    return this.selectedSchedule;
}
Sat.prototype.see = function(scheduleIndex){
    if (this.getSelectedSchedule() == null) 
        return;
    var position = this.getSelectedSchedule().positions[scheduleIndex];
    //position is null
    var elevation = parseFloat(position.elevation);
    var azimuth = parseFloat(position.azimuth);
    wSatMap.moveIcon(parseFloat(position.latitude), parseFloat(position.longitude));
    Player.setIndicator();
    if (Player.currentMode == MODE_STREET) {
        $('satIcon').show();
        var time = Util.parseDateAsGmt(position.time);
        $('satTime').innerHTML = Util.formatDate(time, "HH:mm:ss");
    }
    else 
        if (Player.currentMode == MODE_CANVAS) {
            wCanvas.see(this.getSelectedSchedule().positions, scheduleIndex);
        }
        else {
            $('satIcon').hide();
            $('satTime').innerHTML = '';
        }
    
    var direction = null;
    if (scheduleIndex == 0) {
        direction = Util.getAzimuthByLatLngs(this.getSelectedSchedule().positions[0], this.getSelectedSchedule().positions[this.getSelectedSchedule().positions.length - 1]);
    }
    wEarth.flyTo(position.latitude, position.longitude, direction);
    
    myPOV = {
        yaw: azimuth,
        pitch: -elevation
    };
    street.setPOV(myPOV);
    if (scheduleIndex >= this.getSelectedSchedule().positions.length - 1) 
        Player.stop();
}

Sat.prototype.showDetail = function(){

    $('labelName').innerHTML = this.name || '';
    $('labelOrganization').innerHTML = this.organization || '';
    $('labelLaunchDate').innerHTML = (this.launchDate) ? Util.formatDate(this.launchDate, 'yyyy/MM/DD') : '';
    var photoContainer = $('photoContainer');
    photoContainer.innerHTML = '';
    if (this.photoUrl) {
        var img = document.createElement('img');
        img.src = this.photoUrl;
        photoContainer.appendChild(img);
    }
    $('listUrls').innerHTML = '';
    for (var i = 0, l = this.urls.length; i < l; i++) {
        var li = document.createElement('DIV');
        var link = document.createElement('A');
        link.target = '_blank';
        var val = this.urls[i];
        link.href = val;
        link.innerHTML = val;
        li.appendChild(link);
        $('listUrls').appendChild(li);
    }
}

Sat.prototype.drawSchedule = function(obj){

}

function renderCourse(obj){
    var satCode = obj.basic.satCode;
    var sat = SatMap.getSatByCode(satCode);
    sat.setPolyline(obj);
}

function renderCalendar(obj){
    if (!obj.basic) {
        SatCalendar.showErrorNotAvailable();
        return;
    }
    var satCode = obj.basic.satCode;
    var sat = SatMap.getSatByCode(satCode);
    sat.renderCalendar(obj);
}

/**
 * Initialize the application
 */
var wEarth;
var wSatMap;
var wWorldMap;
var wMap;
var wCanvas;
var wStreet;

function load(){
    Util.loadLocaleFile();
    Sat.DEFAULT_ICON_URL = $('satIconImg').src;
    SAT_LABELS = [$('satName1'), $('satName2')];
    SatMap.loadSatList();
    if (showBoth) {
        $('mapContainer').style.height = '800px';
    }
    
    Event.observe(window, 'resize', resizeAction);
    
    menu = new MenuPanel();
    urlInfo = new Info();
    if (GBrowserIsCompatible()) {
        streetClient = new GStreetviewClient();
        
        wMap = new MapWrapper();
        wSatMap = new SatMapWrapper();
        wWorldMap = new WorldMapWrapper();
        wStreet = new StreetWrapper();
        wEarth = new EarthWrapper();
        wCanvas = new Canvas();
        
        geocoder = new GClientGeocoder();
        setViewMap();
        adjustSatIcon();
        urlInfo.registerPosition(map.getCenter());
    }
    urlInfo.applyFirstPosition();
    $('street').width = $('mapWrapper').style.width;
    $('satIcon').hide();
    $('buttonCloseStreet').hide();
    
    wEarth.show();
    Event.observe(window, 'resize', function(){
        adjustSatIcon()
    });
    
}


function adjustSatIcon(){
    $('street').style.width = $('mapWrapper').style.width;
    var areaOffset = Element.cumulativeOffset($('mapContainer'));
    var satIcon = $('satIcon');
    var closeIcon = $('buttonCloseStreet');
    satIcon.style.top = (areaOffset[1] + 180) + 'px';
    satIcon.style.left = (areaOffset[0] + $('mapContainer').clientWidth / 2) + 'px';
    closeIcon.style.position = 'absolute';
    closeIcon.style.top = (areaOffset[1] + 5) + 'px';
    closeIcon.style.left = (areaOffset[0] + $('mapContainer').clientWidth - 20) + 'px';
}

function refreshStreetStatus(position){
    SatMap.moveTo(position.lat(), position.lng());
}

function setViewMap(){
    Player.currentMode = MODE_MAP;
    wStreet.hide();
    wCanvas.hide();
    wMap.show();
    
    $('satIcon').hide();
    $('buttonCloseStreet').hide();
    map.setCenter(urlInfo.currentPosition);
}

var flashInit = false;

function setViewStreet(latLng){

    wStreet.show(latLng);
}

function setViewCanvas(){
    Player.currentMode = MODE_CANVAS;
    wMap.hide();
    wStreet.hide();
    wCanvas.show();
}

function rotateView(){
    var pov = street.getPOV();
    var yaw = pov.yaw;
    yaw = (yaw + 20) % 360.0;
    pov.yaw = yaw;
    street.panTo(pov);
}

function search(){
    printDebug($('inputAddress').value);
    var address = $('inputAddress').value;
    geocoder.getLatLng(address, function(point){
        if (!point) {
            alert(address + " not found");
        }
        else {
            map.setCenter(point, 13);
            var marker = new GMarker(point);
            map.addOverlay(marker);
            marker.openInfoWindowHtml(address);
        }
    });
}

function showSelect(){
    menu.execListMode();
}

var scheduleIndex = 0;
function debug(){
    SatMap.getSelectedSat().see(scheduleIndex);
    scheduleIndex += 1;
}

function showDetail(){
    menu.execDetailMode();
}

/*
 * Callback Functions
 */
var SELECT_ISS_AUTO = true;

function satArray(obj){
    Message.showMessage(Message.LOADING_SAT_LIST_FINISH);
    $('satListLoading').hide();
    var satList = obj.satellites;
    for (var i = 0; i < satList.length; i++) {
        SatMap.addSat(new Sat(satList[i]));
    }
    if (SELECT_ISS_AUTO) {
        var satCode = urlInfo.accessUrl.getParameter('satCode') || 'ISS';
        var iss = SatMap.getSatByCode(satCode);
        iss.checkbox.checked = true;
        iss.select();
    }
};

function printDebug(msg){
    if (isDebug) {
        Message.showMessage(Message.DEBUG, [msg]);
    }
}

function satSchedule(obj){
    var satCode = obj.basic.satCode;
    var sat = SatMap.getSatByCode(satCode);
    Message.showMessage(Message.LOADING_SAT_SCHEDULE_FINISH, [sat.name]);
    sat.setSchedules(obj);
}

function resizeAction(){
    wEarth.restore();
    wStreet.restore();
    wWorldMap.restore();
    wMap.restore();
    wSatMap.restore();
}
