var blog = {
    'title': "Untitled_" + newKey(5),
    'visibility': 0,
    'key': newKey(),
    'id': null
    //more to come
}

var codes = standard_codes;

var Base = new Class({
    Shift: {'1': false, '2': false},    //tracks if shift or ctrl is pressed
    Pause: false,                       //pauses typing
    pageNum: 0,                         //the current page
    totalPages: 1,                       //the number of pages
    prevLetter: null,                   //the last letter that was typed
    notNow: false,
    
    firstWords: {
        'firstWord': null,
        'firstWordLength': null,
        'lastLetter': null
    },                   
    
    //createKeys builds the wands of the typewriter
    createKeys: function(){
        for (i in codes){
            var rise = codes[i]['y'];
            var rate = -(codes[i]['y'] / codes[i]['x']);
            var container = new Element('div', {
                'class': 'wand_container',
                'id': ('wand_' + codes[i]['label'])
            });
            for (var j=0; j<Math.abs(rise); j+=2){
                var wand_piece = new Element('div', {
                    'class': 'wand_piece',
                    'styles': {
                        'top': j,
                        'left': -(j / rate)
                    }
                });
                container.appendChild(wand_piece);
            }
            $('wands').appendChild(container);
        }
        //and make the wand end
        var wand_head = new Element('img', {
            'id': 'wand_head',
            'src': 'pics/wand_head.png',
            'styles': {
                'left': ((window.getSize().x - 738) / 2) + 355
            }
        });
        document.body.appendChild(wand_head);
    },
    
    //shows then hide the appropriate wand
    showWand: function(code){
        try{
            $('wand_' + codes[code]['label']).setStyle('display', 'block');
            $('wand_head').setStyle('display', 'block');
        } catch(e){ }
    },
    
    removeWand: function(code){
        try{
            $('wand_' + codes[code]['label']).setStyle('display', 'none');
            $('wand_head').setStyle('display', 'none');
        } catch(e){ }
    },
    
    //moves the buttons down and up
    keyPress: function(code){
        try{
            var target = $(codes[code]['label']);
            var top = target.getStyle('top').toInt();
            target.setStyle('top', top.toInt() + 30);
        } catch(e){ }
    },
    
    moveKeyUp: function(code){
        try{
            var target = $(codes[code]['label']);
            var top = target.getStyle('top').toInt();
            target.setStyle('top', top - 30);
        } catch(e){ }
    },
    
    shiftDown: function(num){
        if (this.Shift[num]) return false;
        var target = $('LShift' + num);
        var top = target.getStyle('top');
        target.setStyle('top', top.toInt() + 15);
        (function(){target.setStyle('top', top.toInt() + 30);}).delay(33);
        this.Shift[num] = true;
    },
    
    shiftUp: function(num){
        var target = $('LShift' + num);
        var top = target.getStyle('top');
        target.setStyle('top', top.toInt() - 15);
        (function(){target.setStyle('top', top.toInt() - 30);}).delay(33);
        this.Shift[num] = false;
    },

    addLetter: function(code){
        //don't add a letter if paused.
        if (this.Pause) return false;
        //play sound
        Click.TGotoAndPlay('/','start');    
        //get the character to inject
        try{
            if (code == 32){
                var html = (this.prevLetter == null || this.prevLetter.get('html') == ' ' || this.prevLetter.getElement('br') != null)? '\u00A0': '\u0020';
            } else {
                var html = codes[code]['standard'];
                if (this.Shift['1']) html = codes[code]['ctrl'];
                if (this.Shift['2']) html = codes[code]['shift'];
            }
        } catch(e){ }
        //create the new span
        
        var letter = new Element('span', {
	    'id': newKey(5)
	});
        //add the new letter
        if (this.prevLetter == null){
            letter.inject($('page_' + this.pageNum), 'top');
        } else { 
            letter.inject(this.prevLetter, 'after');
        }
        letter.set('text', html);
        this.prevLetter = letter;
        
        //check if anything should be moved to the next page
        while ($('page_' + this.pageNum).offsetHeight > 570){
            var gotoNextPage =  (this.prevLetter.offsetTop > 570);
            this.dealWithLeftovers(this.pageNum);
            if (gotoNextPage) this.gotoPage('next', this.prevLetter);
        }
    },
    
    newLine: function(){
        if (this.Pause) return false;
        
        Bell.TGotoAndPlay('/','start');  
        this.Pause = true;
        
        (function(){
            var span = new Element('span', {
                'id': newKey(5), 
                'html': '&nbsp;' //this is here so I can get the position of the line break, it just makes things so much easier
            });
            
            span.appendChild(new Element('br'));
            if (this.prevLetter == null){
                span.inject($('page_' + this.pageNum), 'top');
            } else { 
                span.inject(this.prevLetter, 'after');
            }
            this.prevLetter = span;
            this.gotoLast(true);
        }.bind(this)).delay(1000);
            
        (function(){this.Pause = false}.bind(this)).delay(1500);
    },
    
    backspace: function(del){
        //get old last letter, and set new last letter
        if (!del && this.prevLetter == null) return false;
        
        if (del){
            if (this.prevLetter == null && $('page_' + this.pageNum).getElement('span') != null){
                var last = $('page_' + this.pageNum).getElement('span');
            } else if (this.prevLetter != null && this.prevLetter.getNext('span') != null){
                var last = this.prevLetter.getNext('span');
            } else {
                return false;
            }
        } else {
            var last = this.prevLetter;
            if (this.prevLetter.getPrevious('span') != null){
                this.prevLetter = last.getPrevious('span');
            } else { 
                this.prevLetter = null;
            }
        }
        
        
        if (this.firstWords.lastLetter != null){
            //if the current lastLetter is the letter about to be deleted, use the letter before it.
            if (this.firstWords.lastLetter == last) this.firstWords.lastLetter = this.firstWords.lastLetter.getPrevious('span');
            //if that letter is a space, use the letter before it.
            if (this.firstWords.lastLetter.get('html') == ' ') this.firstWords.lastLetter = this.firstWords.lastLetter.getPrevious('span');
        }        
        //delete the last letter
        last.dispose();
        
        try{
            this.checkForGrab(this.pageNum);
        } catch(e){alert(e); return false;}
        
        this.gotoLast();
        return false;
    },
    
    newPage: function(leftovers){
        this.totalPages++;
        $('total_pages').set('html', this.totalPages);
        
        var newPage = new Element('div', {'id': 'page_' + (this.totalPages - 1), 'class': 'page'});
        newPage.adopt(leftovers);
        $('unseenPages').appendChild(newPage);
    },
    
    deletePage: function(page){
        $('page_' + page).dispose();
        this.totalPages--;
        $('total_pages').set('html', this.totalPages);
    },
    
    //controls
    onKeyDown: function(event){
        if (this.notNow) return true;
        
        var e = new Event(event);
        switch (e.code){
            case 13:
                this.newLine();
                break;
            case 16:
                this.shiftDown(2);
                break;
            case 17:
                this.shiftDown(1);
                break;
            case 8:
                this.backspace();
                return false;
            case 46:
                this.backspace(true);
                return false;
            case 37:
                this.move('back');
                break;
            case 38:
                this.move('up');
                break;
            case 39:
                this.move('next');
                break;
            case 40:
                this.move('down');
                break;
            default:
                this.showWand(e.code);
                this.keyPress(e.code);
                this.addLetter(e.code);
                this.gotoLast();
        }
        return false;
    },

    onKeyPress: function(event){
        if (this.notNow) return true;
        
        var e = new Event(event);
        switch (e.code){
            case 8:
                return false;
            default:
                return false;
        }
    },

    onKeyUp: function(event){
        if (this.notNow) return true;
        
        var e = new Event(event);
        switch (e.code){
            case 16:
                this.shiftUp(2);
                break;
            case 17:
                this.shiftUp(1);
                break;
            default:
                this.removeWand(e.code);
                this.moveKeyUp(e.code);
                return false;
        }
        return false;
    },
    
    move: function(direction){
        switch (direction){
            case 'back':
                //null case
                if (this.prevLetter == null) return false;
                //everything else
                if (this.prevLetter.getPrevious('span') != null){
                    this.prevLetter = this.prevLetter.getPrevious('span');
                } else {
                    this.prevLetter = null;
                }
                this.gotoLast();
                break;
            case 'next':
                //if null
                if (this.prevLetter == null && $('page_' + this.pageNum).getElement('span') != null){
                    this.prevLetter = $('page_' + this.pageNum).getFirst('span');
                    this.gotoLast();
                    break;
                }
                //if not null
                if (this.prevLetter.getNext('span') != null){
                    this.prevLetter = this.prevLetter.getNext('span');
                    this.gotoLast();
                } 
                break;
            //TODO consolidate up and down
            case 'up':
                this.getVerticle('up');
                break;
            case 'down':
                this.getVerticle('down');
                break;
            
            default:
                return false;
        }
    },
    
    gotoLetter: function(letter){
        this.prevLetter = letter;
        this.gotoLast();
    },
    
    gotoLast: function(transition){
        //if the last letter is null, just reset
        if (this.prevLetter == null){
            var height = 45;
            var top = 50;
            var left = 185;
        } else {
        //if not, get the coordinates of the last letter
            var height = 15 + this.prevLetter.offsetTop;
            var top = 80 -  this.prevLetter.offsetTop;
            var left = 215 - this.prevLetter.offsetLeft - this.prevLetter.offsetWidth;
            //if the last letter was a normal space then get the cordinates of the letter before it an compenstate for the space
            
            if (this.prevLetter.get('html') == ' '){
                try{
                    height = 15 + this.prevLetter.getPrevious('span').offsetTop;
                    top = 80 - this.prevLetter.getPrevious('span').offsetTop;
                    left = 215 - this.prevLetter.getPrevious('span').offsetLeft - 4 - this.prevLetter.getPrevious('span').offsetWidth;
                } catch(e){alert(e)}
            }
            
            //if the last letter was a linebreak add the height of a new line since the linebreak is
            //actually a part of the preceding line
            if (this.prevLetter.getElement('br') != null){
                height += $('tracker').offsetHeight;
                top -= $('tracker').offsetHeight;
                left = 185;
            }
        }
        //finally actually set the styles. 
        
        $('paper').setStyles({
            'height': height,
            'top': top
        });
        
        if (transition){
            $('bar_container').tween('left', left);
        } else {
            $('bar_container').setStyle('left', left);
        } 
    },
    
    gotoPage: function(page, letter){
        //check for keywords
        if (page == 'next') page = this.pageNum + 1;
        if (page == 'prev') page = this.pageNum - 1;
        if (page == 'last') page = this.totalPages - 1;
        if (page == 'first') page = 1;
        
        //make sure it's a valid page num.
        if (page < 0 || page > this.totalPages -1) return false;
        
        //move the current page to the unseen pages container
        $('unseenPages').appendChild($('page_' + this.pageNum));
        
        //load the page
        $('paper').empty();
        $('paper').appendChild($('page_' + page));
        
        this.pageNum = page;
        $('current_page').set('html', page + 1);
        
        //get the current letter
        var letters = $('page_' + page).getElements('span');
        
        if (letter){
            this.prevLetter = letter;
        } else {
            this.prevLetter = (letters != null)? letters[letters.length - 1]: null;
        }
        
        if ($('bar_container') != null){
            this.gotoLast(true);
        }
    }, 
    
    dealWithLeftovers: function(page){
        var lastWord = this.getWord(page, 'last');
        if ($('page_' + (page + 1)) != null){
            for (var i=lastWord.length - 1; i>=0; i--){
                lastWord[i].inject($('page_' + (page + 1)), 'top');
            }
            while ($('page_' + (page + 1)).offsetHeight >570){
                this.dealWithLeftovers(page + 1);
            }
        } else {
            this.newPage(lastWord);
        }
    },
    
    getWord: function(page, position){
        //set the initial variables
        var page = $('page_' + page).getElements('span');
        var letters = [];
        //get the last word
        if (position == 'last'){
            do {
                var letter =  page.pop();
                letters.unshift(letter);
            }
            while (page[page.length - 1].get('html') != ' ');
        } else if (position = 'first') { //get the first word
            do {
                var letter = page.shift();
                letters.push(letter);
            }
            while (page.length >= 1 && page[0].get('html') != ' ');
        }
        
        return letters;
    }, 
    
    getFirstWord: function(page){
        if (page == (this.totalPages -1)) return false;
        var firstWord = this.getWord(page + 1, 'first');
        var firstWordLength = 0;
        
        var letters = $('page_' + page).getElements('span');
        var lastLetter = letters[letters.length -1];
        if (lastLetter.get('html') == ' ') lastLetter = lastLetter.getPrevious('span');
        
        for (var i=0; i<firstWord.length; i++){
            firstWordLength += firstWord[i].offsetWidth;
        }
        
        //if we're checking the current page we store these values so we don't have to get them on every delete just after a word grab
        if (page == this.pageNum){
            this.firstWords.firstWord = firstWord;
            this.firstWords.firstWordLength = firstWordLength;
            this.firstWords.lastLetter = lastLetter;
        }
        return {'firstWord': firstWord, 'firstWordLength': firstWordLength, 'lastLetter': lastLetter}
    }, 
    
    checkForGrab: function(page, words){
        if ($('page_' + (page + 1)) != null){
            if (!words){
                if (this.firstWords.firstWord == null){
                    words = this.getFirstWord(page);
                } else {
                    words = this.firstWords; 
                }
            }
            
            //TODO: this should be switched to a while statement
            if ((444 - words.lastLetter.offsetLeft + 4) > words.firstWordLength){
                $('page_' + page).adopt(words.firstWord);
                this.firstWords.firstWord = null;
                
                //figure out what to do with the next page
                if ($('page_' + (page + 1)) != null){
                    if ($('page_' + (page + 1)).get('html') == '' || $('page_' + (page + 1)).get('html') == ' '){
                        this.deletePage(page + 1);
                    } else {
                        var newWords = this.getFirstWord(page + 1);
                        this.checkForGrab(page + 1, newWords);
                    }
                }
            }
        }
    },
    
    getVerticle: function(direction){
        //null case
        if (this.prevLetter == null){
            var dir = (direction == 'up')? 'back': 'next';
            this.move(dir);
            return false;
        }
        
        //otherwise...
        if (direction == 'up'){
            //if you're current position is on a break, just move back
            if (this.prevLetter.getElement('br') != null){
                this.move('back');
                return false;
            }
            var prev = this.prevLetter.getAllPrevious('span');
        } else {
            if (this.prevLetter == null || this.prevLetter.getElement('br') != null){
                this.move('next');
                return false;
            }
            var prev = this.prevLetter.getAllNext('span');
        }
        
        //loop through the list of letter until there's a line change
        var prevLetter = (this.prevLetter.get('html') != ' ')? this.prevLetter: this.prevLetter.getPrevious('span');
        
        while (prev.length > 0){
            var current = prev[0];
            if (current.get('html') != ' '){
                if (current.offsetTop != prevLetter.offsetTop) break;
            }
            var castAway = prev.shift();
        }
        //then loop through the remaining letter until the offsetLeft is roughly the same
        if (prev.length > 0){
            var width = $('tracker').offsetWidth / 2;
            //these two variable save info to check for a short line.
            var lastOnLine = (prev[0].get('html') == ' ')? prev[0].getPrevious('span'): prev[0];
            var currentTop = lastOnLine.offsetTop;
            var shortLine = false;
            
            while (prev.length > 0){
                var current = prev[0];
                if (current.get('html') != ' '){
                    if (current.offsetLeft - prevLetter.offsetLeft < 5 && current.offsetLeft - prevLetter.offsetLeft > -5) break;
                } else {
                    //add in a little more tolerance for spaces.
                    if (current.offsetLeft - prevLetter.offsetLeft < 10 && current.offsetLeft - prevLetter.offsetLeft > -10) break;
                }
                var castAway = prev.shift();
                if (prev.length == 0 || prev[0].offsetTop != currentTop){
                    if (direction == 'down') lastOnLine = castAway;
                    shortLine = true;
                    break;
                }
            }
            
            if (shortLine){
                prev[0] = lastOnLine;
            }
            
        }
        
        if (prev.length > 0){
            var letter = (prev[0].getElement('br') != null && prev[0].getPrevious('span') != null)? prev[0].getPrevious('span'): prev[0];
            this.prevLetter = letter;
            this.gotoLast();
        }
    }
});

//Loading and saving
Base.implement({
    waiting: false,
    
    //TODO: this needs to be updated, god only knows what it'll do right now
    saveBlog: function(){
        var request = blog;
        var pages = this.getPages();
        if (pages.length != this.totalPages){
            alert('nope');
       	    return false;
        }
        request['pages'] = this.getPages();
        request['owner'] = location.toString().split('/')[5] || 'guest';
        var options = {
            method:    'post',
            url:       'php/saveBlog.php',
            data:      request,
            onRequest: function() {
                var result = Notification('Saving', false, {'delay': 'wait'});
            },
            onSuccess: function(resp) {
                var result = Notification('Saved', false);
                blog.id = resp;
            }, 
            onFailure: function(resp){
                //alert(resp);
            }
        };
        
        var submit = new Request(options).send();
    },
    
    saveTitle: function(){
        if ($('newTitle').get('value').clean() != ''){
            $('blog_title').set('html', $('newTitle').get('value'));
            blog.title = $('newTitle').get('value');
            if (blog.id != null){
                var result = new Model('blogs').Update(['title'], [blog.title], 'id=' + blog.id).Eval();
            }
        }
    },
    
    saveVisibility: function(){
        blog.visibility = $('blog_visibility').get('value');
        if (blog.id != null){
            var result = new Model('blogs').Update(['visibility'], [blog.visibility], 'id=' + blog.id).Eval();
        }
    },
    
    waitForSave: function(){
        
    }, 
    
    getPages: function(){
        var data = [];
        if ($('unseenPages').getElements('div') == null){
        	var pages = [];
        } else {
            var pages = $('unseenPages').getElements('div');
        }
        pages.push($('paper').getElement('div'));
        for (var i=0; i<pages.length; i++){
            data.push({
                'page_num': pages[i].id.split('_')[1],
                'content': pages[i].get('html')
            });
        }
        return data;
    }, 
    
    loadBlog: function(blogInfo){
        for (var i=0; i<blogInfo.length; i++){
            var page = new Element('div', {
                'id': 'page_' + blogInfo[i]['page_num'],
                'html': blogInfo[i]['content']
            });
            if (i == 0){
                $('paper').empty();  
                $('paper').appendChild(page);
            } else {
                $('unseenPages').appendChild(page);
            }
        }
    }
});

var Typewriter = new Class({
    Extends: Base,
    
    initialize: function(){
    
    }
});
//TODO move all these

function show_tabs(){
    $('tool_container').set('tween', {'duration': 100});
    $('tool_container').tween('width', 170);
    $('content_mask').set('morph', {'duration': 100});
    $('content_mask').morph({'display': 'block', 'opacity': 0.6});
    typewriter.notNow = true;
}

function hideTools(event){
    var e = new Event(event);
    if ($(e.target).getParent('#toolbar') == null && $(e.target).getParent('#notification') == null){
        $('tool_container').set('tween', {'duration': 100});
        $('tool_container').tween('width', 5);
        $('content_mask').morph({'opacity': 0});
        typewriter.notNow = false;
        (function(){
            $('content_mask').setStyle('display', 'none');
            if ($('notification') != null) $('notification').destroy();
        }).delay(100);
    }
}

function openTitleEditor(){
    Notification(blog.title, false, {'delay': 'wait', 'xml': 'change_title.xml'});
}

//TODO move
function cancelNotification(){
    $('notification').dispose();
}

function setBlogData(){
    $('blog_title').set('html', blog.title);
    $('blog_visibility').set('value', blog.visibility);
}