/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 	
 */


/* 'downloads' for debugging, 
 * 'wgt-private' for any release */
var STORAGE_SPACE = "downloads";
var SHORT_WAIT = 10000;
var LONG_WAIT = 30000; // 600000
var DESIGN = 1; // 0 - longitude, 1 - mission control
var wait_time = LONG_WAIT;
var events = null;
var deviceFingerprint = null;
var eventsTimeStamp = 0;
var flagDigital = true;

(function() {
    var canvasLayout,
        canvasContent,
        ctxLayout,
        ctxContent,
        center,
        watchRadius;
    
   var flagConsole = true;
    
    
    function deleteFile(name, callback) {
    	
    	/* successful resolution of wgt-private */
    	function onsuccess(dir){
            var dirfile = null;
            try {
            	dirfile = dir.resolve(name);
            } catch (exc) {
            	console.log(exc.message)
            }
            
            // if file not existed, call callback with null
            if(dirfile == null) {
            	callback();
            	return;
            } else {
            
            	// if file was found, delete it
            	dir.deleteFile(dirfile.fullPath,  function() {
            		console.log("deleted");
            		callback();
            	},  
            	function(e) {console.log(e)});
            }
    	}
        
        function onerror(e){
        	console.log("error"+e);
        }
        
        function onsuccessPermission(){
        	tizen.filesystem.resolve(STORAGE_SPACE, onsuccess, onerror, "rw");
        }
        
        function onErrorPermission(e){
        	console.log("error "+ JSON.stringify(e));
        }
        
    	tizen.ppm.requestPermission("http://tizen.org/privilege/mediastorage", onsuccessPermission, onErrorPermission);
    }
    
    function clearDownloads() {
        var onError = function(e) {
     	   console.log('Error!' + e.message);
     	 };

     	 var onResolveSuccess = function(dir) {
     	   var onListFilesSuccess = function(files) {
     	     files.forEach(function(file) {
     	       if (!file.isDirectory) {
     	         dir.deleteFile(file.fullPath, onDeleteSuccess, onError);
     	       }
     	     });
     	   };
     	   
     	   dir.listFiles(onListFilesSuccess, onError);
     	 };

     	 var onDeleteSuccess = function() {};

        tizen.filesystem.resolve('/opt/usr/media/Downloads', onResolveSuccess, onError);

    }

    function getJsonFile(name, callback) {
    	console.log("searching for File");
    	/* successful resolution of wgt-private */
    	function onsuccess(dir){
            var dirfile = null;
            try {
            	dirfile = dir.resolve(name);
            } catch (exc) {
            	console.log(exc.message)
            }
            
            // if file not existed, call callback with null
            if(dirfile == null) {
            	callback(null)
            	return;
            } else {
            
            	// if file was found, call callback with json
            dirfile.openStream("r", function(fs) {
            	callback(JSON.parse(fs.read(dirfile.fileSize)));
        		fs.close();
                });
            }
    	}
        
        function onerror(e){
        	console.log("error"+e);
        }
        
        function onsuccessPermission(){
        	tizen.filesystem.resolve(STORAGE_SPACE, onsuccess, onerror, "rw");
        }
        
        function onErrorPermission(e){
        	console.log("error "+ JSON.stringify(e));
        }
        
    	tizen.ppm.requestPermission("http://tizen.org/privilege/mediastorage", onsuccessPermission, onErrorPermission);
    }
    
    function readJSON() {
    	/* - requests permission to view media storage
    	 * - resolves the file 'calendarevents'
    	 * - opens stream and reads entire file as json
    	 */
    	
    	getJsonFile("calendarevents", function(eventlist) {events = eventlist;});
    }
    
    function getNewFingerprintFromServer() {
    	getFileFromServer("devicefingerprint.json", function() {
    		console.log("getting device fingerprint from server");
    		getJsonFile("devicefingerprint", function(df) {deviceFingerprint = df; console.log(deviceFingerprint);});
    	});
    }
    

    function getDeviceFingerprint() {
    	// check if device id set already as global, if it is return
    	if(deviceFingerprint !== null) {
    		return;
    	}
    	
    	// otherwise, check if there is a devicefingerprint file in wgt-private 
    	// if there is, open it up and get your device ID from it
    	getJsonFile("devicefingerprint", function(df) {
    		if(df !== null) {
    			deviceFingerprint = df;
    		} else {
    			// otherwise, ask the longitude server for a device fingerprint file
    			getNewFingerprintFromServer();
    		}
    	});
    	
    }
    
    function getFileFromServer(route, callback) {
    	console.log("getting file");
    	var server = "https://longitudecalendar.com/";
    	var downloadRequest = new tizen.DownloadRequest(server + route, STORAGE_SPACE);
    	tizen.systeminfo.getPropertyValue('NETWORK', function(networkInfo) {
        if (networkInfo.networkType === 'NONE') {
            console.log('Network connection is not available.Download is not possible.');
            downloadRequest = null;
        }
    });
    
    var listener = {
            /* When the download progresses (interval is platform-dependent) */
            onprogress: function(id, receivedSize, totalSize) {
            	console.log('progress for id: ' + id);
            },

            /* When the user pauses the download */
            onpaused: function(id) {
                console.log('Paused with id: ' + id);
            },

            /* When the user cancels the download */
            oncanceled: function(id) {
                console.log('Canceled with id: ' + id);
            },

            /* When the download is completed */
            oncompleted: function(id, fullPath) {
                console.log('Completed with id: ' + id + ', full path: ' + fullPath);
                callback();
            },

            /* When the download fails */
            onfailed: function(id, error) {
                console.log('Failed with id: ' + id + ', error name: ' + error.name);
            }
        };
    tizen.download.start(downloadRequest, listener);
    }
    
    function updateCalendar() {
    	
    	if(deviceFingerprint === null) {
    		console.log("no fingerprint, loading from file or server");
    		getDeviceFingerprint();
    		wait_time = SHORT_WAIT;
    		return false;
    	}
    		
    	function deleteCallback() {
    		console.log("done");
    		console.log("getting new calendar events");
    		getFileFromServer("device/" + deviceFingerprint.deviceName + "/calendarevents.json", readJSON);
    	}
    	
    	console.log("deleting calendarevents");
		deleteFile("calendarevents", deleteCallback);
        	
    	return true;
    }
    
    /**
     * Renders a circle with specific center, radius, and color
     * @private
     * @param {object} context - the context for the circle to be placed in
     * @param {number} radius - the radius of the circle
     * @param {string} color - the color of the circle
     */
    function renderCircle(context, center, radius, color) {
        context.save();
        context.beginPath();
        context.fillStyle = color;
        context.arc(center.x, center.y, radius, 0, 2 * Math.PI);
        context.fill();
        context.closePath();
        context.restore();
    }
    
    /**
     * Renders a circle with specific center, radius, and color
     * @private
     * @param {object} context - the context for the circle to be placed in
     * @param {number} radius - the radius of the circle
     * @param {string} color - the color of the circle
     */
    function renderRing(context, center, radius, width, color) {
        context.save();
        context.beginPath();
        context.arc(center.x, center.y, radius, 0, 2 * Math.PI);
        context.strokeStyle = color;
        context.lineWidth = width;
        context.stroke();
        context.closePath();
        context.restore();
    }
    

    function renderSimpleArc(context, center, radius, thickness, color, startAngle, endAngle) {
    	context.save();
        context.beginPath();
        
        context.arc(center.x, center.y, radius, startAngle * Math.PI / 180., endAngle * Math.PI / 180.);
        context.fillStyle = color;
        context.strokeStyle = color;
        context.lineWidth = thickness;
        context.stroke();        
        context.restore();
    }
    /**
     * Renders a partial with specific center, radius, and color
     * @private
     * @param {object} context - the context for the circle to be placed in
     * @param {number} radius - the radius of the circle
     * @param {string} color - the color of the circle
     */
    function renderArc(context, center, radius, thickness, color, startAngle, endAngle, hardStart, hardStop) {
    	
    	if(hardStart == undefined) {
    		hardStart = false;
    	}
    	if(hardStop == undefined) {
    		hardStop= false;
    	}
        
        var seperation = 3;
        // if arc size smaller than separation for circles or circle,
        // draw a circle
        
        var arcsize = 0;
        if(endAngle < startAngle) {
        	arcsize = 360 - startAngle + endAngle;
        } else {
        	arcsize = endAngle - startAngle;
        }
        
        if(arcsize < 2*seperation) {
        	renderCircle(context, polToCart(radius, startAngle + arcsize /2 ), 9, color);
        	return;
        }
        
        if(!hardStart) {
        	startAngle += seperation;
        }
        
        if(startAngle >= 360) {
        	startAngle -= 360;
    	}
        
        if(!hardStop) {
        	endAngle -= seperation;
    	}
        
        if(endAngle<0) {
        	endAngle += 360;
        }
        
        // otherwise draw an arc and two circles
        context.save();
        context.beginPath();
        
        context.arc(center.x, center.y, radius, startAngle * Math.PI / 180., endAngle * Math.PI / 180.);
        context.fillStyle = color;
        context.strokeStyle = color;
        context.lineWidth = thickness;
        context.stroke();        
        context.restore();
        
        if(!hardStart)
        	renderCircle(context, polToCart(radius, startAngle), 9, color);
        if(!hardStop)
        	renderCircle(context, polToCart(radius, endAngle), 9, color);
    }
    
    function polToCart(radius, angle) {
    	pos = {
                x: center.x + radius * Math.cos(angle * Math.PI / 180),
                y: center.y + radius * Math.sin(angle * Math.PI / 180)
            };
    	return pos;
    }
    
    function hourToAngle(hour) {
    	if(hour >= 18)
    		hour -= 18;
    	else
			hour += 6;
    	var angle = (hour) * 15;
    	
    	return angle;
    }
    
    function minuteToAngle(minute) {
    	if(minute >= 15)
    		minute -= 15;
    	else
			minute += 45;
    	angle = (minute / 60) * 360;
    	
    	return angle;
    }

    /**
     * Renders a needle with specific center, angle, start point, end point, width and color
     * @private
     * @param {object} context - the context for the needle to be placed in
     * @param {number} angle - the angle of the needle (0 ~ 360)
     * @param {number} startPoint - the start point of the needle (-1.0 ~ 1.0)
     * @param {number} startPoint - the end point of the needle (-1.0 ~ 1.0)
     * @param {number} width - the width of the needle
     * @param {string} color - the color of the needle
     */
    function renderNeedle(context, angle, startPoint, endPoint, width, color) {
        var radius = context.canvas.width / 2,
            centerX = context.canvas.width / 2,
            centerY = context.canvas.height / 2,
            dxi = radius * Math.cos(angle) * startPoint,
            dyi = radius * Math.sin(angle) * startPoint,
            dxf = radius * Math.cos(angle) * endPoint,
            dyf = radius * Math.sin(angle) * endPoint;

        context.save();
        context.beginPath();
        context.lineWidth = width;
        context.strokeStyle = color;
        context.moveTo(centerX + dxi, centerY + dyi);
        context.lineTo(centerX + dxf, centerY + dyf);
        context.stroke();
        context.closePath();
        context.restore();
    }
    
    /** 
     * Renders the 24h sun around the watch
     */
    function renderSun(date, hour, minute, second) {
    	
    	if(hour > 6 && hour < 18)
    		sunColor = "#FFD700";
    	else {
			sunColor = "#C0C0C0"
		}
    	
    	sunDistance = document.body.clientWidth / 2 - 60;
        renderCircle(ctxContent, polToCart(sunDistance, hourToAngle(hour + minute / 60)), 16, sunColor);    	
    }
    
    function renderEarth(date, minute, second) {
    	
		var earthColor = "#0077BE";
    	var earthDistance = document.body.clientWidth / 2 - 120;
        renderCircle(ctxContent, polToCart(earthDistance, minuteToAngle(minute + second / 60)), 10, earthColor);    	
    }


    /**
     * Renders text at a specific center, radius, and color
     * @private
     * @param {object} context - the context for the text to be placed in
     * @param {string} text - the text to be placed
     * @param {number} x - the x-coordinate of the text
     * @param {number} y - the y-coordinate of the text
     * @param {number} textSize - the size of the text in pixel
     * @param {string} color - the color of the text
     */
    function renderText(context, text, x, y, textSize, color) {
        context.save();
        context.beginPath();
        context.font = textSize + "px Courier";
        context.textAlign = "center";
        context.textBaseline = "middle";
        context.fillStyle = color;
        context.fillText(text, x, y);
        context.closePath();
        context.restore();
    }

    /**
     * Draws the basic layout of the watch
     * @private
     */
    function drawWatchLayout() {

        // Clear canvas
        ctxLayout.clearRect(0, 0, ctxLayout.canvas.width, ctxLayout.canvas.height);
        
        // Draw the background circle
        /*
        renderCircle(ctxLayout, center, watchRadius, "#000000");
        grd = ctxLayout.createLinearGradient(0, 0, watchRadius * 2, 0);
        grd.addColorStop(0, "#000000");
        grd.addColorStop(0.5, "#454545");
        grd.addColorStop(1, "#000000");
        ctxLayout.fillStyle = grd;
        renderCircle(ctxLayout, center, watchRadius * 0.945, grd);
        renderCircle(ctxLayout, center, watchRadius * 0.7, "#000000");
		*/
        // Draw the dividers
        // 60 unit divider
        /*
        for (i = 1; i <= 60; i++) {
            angle = (i - 15) * (Math.PI * 2) / 60;
            renderNeedle(ctxLayout, angle, 0.95, 1.0, 1, "#c4c4c4");
        }

        // 12 unit divider
        for (j = 1; j <= 12; j++) {
            angle = (j - 3) * (Math.PI * 2) / 12;
            renderNeedle(ctxLayout, angle, 0.7, 0.945, 10, "#c4c4c4");
        }
		*/
        
        // renderText(ctxLayout, "TIZEN WATCH", center.x, center.y - (watchRadius * 0.4), 25, "#999999");
    }

    /**
     * Draws the content of the watch
     * @private
     */
    function drawWatchContent() {
        var datetime = tizen.time.getCurrentDateTime();

        // Clear canvas
        ctxContent.clearRect(0, 0, ctxContent.canvas.width, ctxContent.canvas.height);
        
    	
    	var hour = datetime.getHours(),
        	minute = datetime.getMinutes(),
        	second = datetime.getSeconds(),
        	date = datetime.getDate();
    	
    	if(DESIGN === 0) {
            renderSun(date, hour, minute, second);
            renderEarth(ctxContent, minute, second);
       } else if(DESIGN === 1) {
        	drawMissionControl();
        }
    	

        /* if no return from server yet */
        if(events === null) {
        	return;
        }
        
        /* if device not on server anymore */
        if(events.kind === "not found") {
        	deviceFingerprint = null;
        	events = null;
        	deleteFile("devicefingerprint", function() {});
        	wait_time = SHORT_WAIT;
        	return;
        }
        /* if device not registered */
        if(events.kind === "unregistered") {
        	wait_time = SHORT_WAIT;
        	if(deviceFingerprint === null) {
        	} else {
        		renderText(ctxContent, deviceFingerprint.deviceName, center.x, center.y, 20, "FF0000");
        	}
        	return;
        }
       
        if(DESIGN === 0) {
        	var thickness = 18;
        } else if(DESIGN === 1) {
        	var thickness = 50;
        }
        
        console.log("switched to long wait");
        wait_time = LONG_WAIT;
        
        
        for(var event in events.events){
        	var startedBeforeToday = false;
        	var endsAfterToday = false;
        	var e = events.events[event];
        	// check if not today
        	if(e.startDateTime.date.year !== tizen.time.getCurrentDateTime().getFullYear() ||
        	   e.startDateTime.date.month !== tizen.time.getCurrentDateTime().getMonth() + 1 || 
        	   e.startDateTime.date.day !== tizen.time.getCurrentDateTime().getDate()){
        		
        		// if not today, check if it is an earlier event
        		if(e.startDateTime.date.year < tizen.time.getCurrentDateTime().getFullYear()) {
        			startedBeforeToday = true;
        		} else if(e.startDateTime.date.month < tizen.time.getCurrentDateTime().getMonth() + 1) {
        			startedBeforeToday = true;
        		} else if(e.startDateTime.date.day < tizen.time.getCurrentDateTime().getDate()) {
        			startedBeforeToday = true;
        		} else {
        			continue;
        		}
        	}

        	// check if not today
        	if(e.stopDateTime.date.year !== tizen.time.getCurrentDateTime().getFullYear() ||
        	   e.stopDateTime.date.month !== tizen.time.getCurrentDateTime().getMonth() + 1 || 
        	   e.stopDateTime.date.day !== tizen.time.getCurrentDateTime().getDate()){
        		
        		// if not check if later date
        		if(e.stopDateTime.date.year > tizen.time.getCurrentDateTime().getFullYear()) {
        			endsAfterToday = true;
        		} else if(e.stopDateTime.date.month > tizen.time.getCurrentDateTime().getMonth() + 1) {
        			endsAfterToday = true;
        		} else if(e.stopDateTime.date.day > tizen.time.getCurrentDateTime().getDate()) {
        			endsAfterToday = true;
        		} else {
        			continue;
        		}
        	}
        	if(startedBeforeToday && endsAfterToday) {
        		continue;
        	}
        	
        	var startTime = 0;
        	if(!startedBeforeToday) {
        		startTime = e.startDateTime.time.hour + e.startDateTime.time.minute / 60;
        	}
        	var stopTime = 24;
        	if(!endsAfterToday) {
        		stopTime = e.stopDateTime.time.hour + e.stopDateTime.time.minute / 60;
        	}

        	if(DESIGN === 0) {
                var edge = document.body.clientWidth / 2 - thickness / 2 - 2;
        		renderArc(ctxContent, center, edge, thickness, e.color, hourToAngle(startTime), hourToAngle(stopTime), startedBeforeToday, endsAfterToday);     	
        	} else if(DESIGN === 1) {
                var edge = document.body.clientWidth / 2 - thickness / 2 - 2;
        		renderArc(ctxContent, center, edge, thickness, e.color, hourToAngle(startTime), hourToAngle(stopTime), true, true);   
        	}
    	}
        
        if(DESIGN === 0) {
    	} else if(DESIGN === 1) {
    		var inner = document.body.clientWidth / 2 - thickness;
    	    renderSimpleArc(ctxContent, center, 115, 8, "#FFFFFF", hourToAngle(20), hourToAngle(20+0.999))
    	    for(var i = 0; i < 24; i++) {
    	    	renderSimpleArc(ctxContent, center, edge, thickness, "#000000", i*15-0.8, i*15+0.8);
    	    }
    	}

       
    }
    
    function drawMissionControl(datetime) {
            var strHours = document.getElementById("str-hours"),
                strConsole = document.getElementById("str-console"),
                strMinutes = document.getElementById("str-minutes"),
                datetime = tizen.time.getCurrentDateTime(),
                hour = datetime.getHours(),
                minute = datetime.getMinutes();

            strHours.innerHTML = hour;
            strMinutes.innerHTML = minute;

            if (hour < 10) {
                strHours.innerHTML = "0" + hour;
            }
            if (minute < 10) {
                strMinutes.innerHTML = "0" + minute;
            }

            // Each 0.5 second the visibility of flagConsole is changed.
            if(flagDigital) {
                if (flagConsole) {
                    strConsole.style.visibility = "visible";
                    flagConsole = false;
                } else {
                    strConsole.style.visibility = "hidden";
                    flagConsole = true;
                }
            }
            else {
                strConsole.style.visibility = "visible";
                flagConsole = false;
            }
        }

        /**
         * Sets to background image as BACKGROUND_URL,
         * and starts timer for normal digital watch mode.
         * @private
         */
        function initDigitalWatch() {
            flagDigital = true;
            document.getElementById("digital-body").style.backgroundImage = BACKGROUND_URL;
            interval = setInterval(updateTime, 500);
        }
    
    function loopCalendar(offset_ms) {
    	var d = new Date();
    	var currentTime = d.getTime();
    	if(eventsTimeStamp + offset_ms < currentTime){
    		updateCalendar();
			eventsTimeStamp = currentTime;
		}
    	
    	
    }

    /**
     * Set default variables
     * @private
     */
    function setDefaultVariables() {
        canvasLayout = document.querySelector("#canvas-layout");
        ctxLayout = canvasLayout.getContext("2d");
        canvasContent = document.querySelector("#canvas-content");
        ctxContent = canvasContent.getContext("2d");

        // Set the canvases square
        canvasLayout.width = document.body.clientWidth;
        canvasLayout.height = canvasLayout.width;
        canvasContent.width = document.body.clientWidth;
        canvasContent.height = canvasContent.width;

        center = {
            x: document.body.clientWidth / 2,
            y: document.body.clientHeight / 2
        };

        watchRadius = canvasLayout.width / 2;
    }

    /**
     * Set default event listeners
     * @private
     */
    function setDefaultEvents() {
        // add eventListener to update the screen immediately when the device wakes up
        document.addEventListener("visibilitychange", function() {
            if (!document.hidden) {
                // Draw the content of the watch
                drawWatchContent();
            }
        });
    }

    /**
     * Initiates the application
     * @private
     */
    function init() {
    	// clearDownloads();
        setDefaultVariables();
        setDefaultEvents();

        // Draw the basic layout and the content of the watch at the beginning
        drawWatchLayout();
        drawWatchContent();

        // Update the content of the watch every second
        setInterval(function() {
            drawWatchContent();
            loopCalendar(wait_time);
        }, 1000);
    }

    window.onload = init;
}());

document.addEventListener('ambientmodechanged', function(ev) {
    var mode = ev.detail.ambientMode;
    if (mode == true) {
        /* Change the UI for ambient mode */

       //updateAmbientWatchFaceDOM(); // function to rearange DOM for AOD mode
    	flagDigital = false;
        updateTime();
    } else {
        /* Change the UI for normal mode */

       // updateNormalWatchFaceDOM(); // function to rearange DOM for AOD mode
    	flagDigital = true;
       updateTime();
    }
});

document.addEventListener('timetick', function(ev) {
    /* Update the UI */

	drawWatchContent();
});