/*-
 * $Id: creepy.js,v 1.25 2011-10-07 16:27:32 justme Exp $
 *
 * Copyright (c) 2007 by Aparzev GmbH (http://www.aparzev.com).
 *
 * 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.
 *
 */
var ENUM_TO_HTML = {
    "fontWeight": {
        "NORMAL": "normal",
        "BOLD": "bold"
    },
    "fontStyle": {
        "NORMAL": "normal",
        "ITALIC": "italic"
    },
    "textDecoration": {
        "OVERLINE": "overline",
        "UNDERLINE": "underline",
        "BLINK": "blink",
        "NONE": "none",
        "LINE_THROUGH": "line-through"
    },
    "fontVariant": {
        "NORMAL": "normal",
        "SMALLCAPS": "small-caps"
    },
    "textTransform": {
        "UPPERCASE": "uppercase",
        "CAPITALIZE": "capitalize",
        "NONE": "none",
        "LOWERCASE": "lowercase"
    },
    "fontFamily": {
        "MONO": "'Courier New', Courier, mono",
        "SERIF": "'Times New Roman', Times, Serif",
        "SANSSERIF": "Arial, Helvetica, Lucida, 'Sans Serif'",
        "VERDANA": "Verdana, Lucida, Arial, Helvetica, 'Sans Serif'"
    }
};

var CSS_UNIT_TO_HTML = {
    "INCH": "in",
    "PERCENT": "%",
    "PIXEL": "px",
    "CENTIMETER": "cm",
    "POINT": "pt",
    "EM": "em",
    "EX": "ex",
    "MILLIMETER": "mm"
}

var TRANSLATE = {

    TO_JAVASCRIPT: {
        "font-family": "fontFamily",
        "font-size": "fontSize",
        "font-weight": "fontWeight",
        "font-style": "fontStyle",
        "font-variant": "fontVariant",
        "text-decoration": "textDecoration",
        "text-transform": "textTransform",
        "letter-spacing": "letterSpacing",
        "color": "color",
        "background-color": "backgroundColor",
        "background-image": "backgroundImage",
        "margin-top": "marginTop",
        "margin-bottom": "marginBottom",
        "margin-left": "marginLeft",
        "margin-right": "marginRight",
        "padding-top": "paddingTop",
        "padding-bottom": "paddingBottom",
        "padding-left": "paddingLeft",
        "padding-right": "paddingRight"
    },
    
    TO_HTML: {
        "fontFamily": "font-family",
        "fontSize": "font-size",
        "fontWeight": "font-weight",
        "fontStyle": "font-style",
        "fontVariant": "font-variant",
        "textDecoration": "text-decoration",
        "textTransform": "text-transform",
        "letterSpacing": "letter-spacing",
        "color": "color",
        "backgroundColor": "background-color",
        "backgroundImage": "background-image",
        "borderTop": "border-top",
        "borderBottom": "border-bottom",
        "borderLeft": "border-left",
        "borderRight": "border-right",
        "marginTop": "margin-top",
        "marginBottom": "margin-bottom",
        "marginLeft": "margin-left",
        "marginRight": "margin-right",
        "paddingTop": "padding-top",
        "paddingBottom": "padding-bottom",
        "paddingLeft": "padding-left",
        "paddingRight": "padding-right",
        "lineHeight": "line-height"
    },
    
    toJavascript: function(value) {
        return this.TO_JAVASCRIPT[value];
    },
    
    toHTML: function(value) {
        return this.TO_HTML[value];
    }
}

function Creepy(splittingStrategy, textAlign) {
    this.splittingStrategy = splittingStrategy;
    this.textAlign = textAlign || "center"; // hack for downward compatibility
    this.policies = [];
    this.errors = [];
}

Creepy.prototype.addPolicy = function(policy) {

    this.policies.push(policy);
}

Creepy.prototype.getTextAlign = function() {

    return this.textAlign;
}

Creepy.prototype.processText = function(content) {

    var items = this.splittingStrategy.split(content);
    var innerHTML = "";
    
    for (var i = 0; i < items.length; i++) {
    
        var item = items[i];
        
        if (item == "\n" || item == "<br />" || item == "<br/>") {
            //alert("Line break found");
            innerHTML += "<br />";
            continue;
        }
        
        if (item == " ") {
            //alert("Empty space found");
            item = "&#160;";
        }
        
        var styleChunk = "";
        
        for (var j = 0; j < this.policies.length; j++) {
        
            var policy = this.policies[j];
            
            if (policy.isEmpty() || policy.isHomogenous()) {
                continue;
            }
            
            styleChunk += this.appendStyle(policy);
        }
        
        if (styleChunk.length > 0) {
            innerHTML += '<span style="' + styleChunk + '">' + item + '</span>';
        }
        else {
            innerHTML += item;
        }
    }
    
    //alert(innerHTML);
    return innerHTML;
}

Creepy.prototype.appendHomogenous = function(containter) {

    var style = containter.style;
    
    for (var j = 0; j < this.policies.length; j++) {
    
        var policy = this.policies[j];
        
        if (policy.isEmpty() || !policy.isHomogenous()) {
            continue;
        }
        
        style[policy.getName()] = policy.getValue();
    }
}

Creepy.prototype.appendStyle = function(policy) {

    var styleChunk = "";
    
    var translated = TRANSLATE.toHTML(policy.getName());
    
    if (translated == null) {
        throw new Error("Property " + policy.getName() + " cannot be translated");
    }
    
    var value = policy.getValue();
    
    styleChunk += translated + ":" + policy.getValue() + ";";
    
    return styleChunk;
}

Creepy.prototype.process = function(elem) {

    if (elem == null) {
        throw new Error("Element not found!");
    }
    
    if (this.policies.length == 0) {
        alert("No policies found!");
        return;
    }    
    
    elem.style.cssText = "";
    elem.style.textAlign = this.textAlign;
    this.appendHomogenous(elem);
    
    var content = elem.innerHTML;
    elem.innerHTML = "";
    
    var processed = this.processText(content);
    elem.innerHTML = processed;
    
    //alert("Processed OK");
}

Creepy.prototype.getErrors = function() {
    return this.errors.join("\n");
}

Creepy.prototype.getErrorsCount = function() {
    return this.errors.length;
}

/**********************************************************************
 *                            Strategies                              *
 **********************************************************************/
var CreepyStrategy = Class.create();

CreepyStrategy.prototype = {

    initialize: function(property, value) {
    
        if (property == null) {
            throw new Error("Property name must not be undefined");
        }
        
        this.property = property;
        this.value = value;
        this.empty = false;
        this.checkValue();
    },
    
    getName: function() {
        return this.property;
    },
    
    toString: function() {
        return this.property + " -> " + this.value;
    },
    
    isEmpty: function() {
        return this.empty;
    }
}

var CreepyValues = Class.create();

CreepyValues.prototype = {

    checkValue: function() {
        if (this.value.length == 0) {
            this.empty = true;
        }
    },
    
    isHomogenous: function() {
        return this.value.length == 1 && this.property != "backgroundColor";
    },
    
    getValue: function() {
    
        var random = new RandomRange(0, this.value.length - 1);
        var rand = random.next();
        if (rand >= this.value.length) {
            throw new Error(rand + " >= " + this.value.length);
        }
        return this.value[rand];
    }
}

Object.extend(CreepyValues.prototype, CreepyStrategy.prototype);

// Creepy policy

var CreepyValuesRef = Class.create();

CreepyValuesRef.prototype = {

    checkValue: function() {
        if (this.value.length == 0) {
            this.empty = true;
        }
    },
    
    isHomogenous: function() {
    
        return this.value.length == 1 && this.property != "backgroundColor";
    },
    
    getValue: function() {
    
        if (this.empty) {
            return null;
        }
        
        var length = this.value.length;
        var random = new RandomRange(0, length - 1);
        var rand = random.next();
        
        if (rand >= length) {
            throw new Error(rand + " >= " + length);
        }
        
        var index = this.value[rand];
        var ref = creepyReference[this.property].value;
        
        if (ref == null) {
            throw new Error("Property " + this.property + " not found in the styles catalogue");
        }
        
        if (index >= ref.length) {
            throw new Error("Array index out of bounds for property " + property + ": index: " + index + ", size: " + ref.length);
        }
        
        var ret = ref[index]
        
        if (ret == null) {
            throw new Error("No value of " + this.property + " found at index " + index + ", avaliable: " + ref.join(","));
        }
        
        return ret;
    }
}

Object.extend(CreepyValuesRef.prototype, CreepyStrategy.prototype);

var CreepyRange = Class.create();

CreepyRange.prototype = {

    checkValue: function() {
        return;
    },
    
    isHomogenous: function() {
        var range = this.value;
        return range.min == range.max && this.property != "backgroundColor";
    },
    
    getValue: function() {
        var range = this.value;
        var random = new RandomRange(range.min, range.max);
        var next = random.next();
        return random.next() + range.unit;
    }
}

Object.extend(CreepyRange.prototype, CreepyStrategy.prototype);

/**********************************************************************
 *                          Style Appenders                           *
 **********************************************************************/
var StyleAppender = Class.create();

StyleAppender.prototype = {

    initialize: function(container) {
        this.container = container;
    }
}

var HtmlStyleAppender = Class.create();

HtmlStyleAppender.prototype = {

    processText: function(items, policies) {
    
        var innerHTML = "";
        
        for (var i = 0; i < items.length; i++) {
        
            var item = items[i];
            
            if (item == "\n" || item == "<br />" || item == "<br/>") {
                //alert("Line break found");
                innerHTML += "<br />";
                continue;
            }
            
            var styleChunk = "";
            
            for (var j = 0; j < this.policies.length; j++) {
            
                var policy = this.policies[j];
                
                if (policy.isEmpty() || policy.isHomogenous()) {
                    continue;
                }
                
                styleChunk += this.appendStyle(policy);
            }
            
            if (styleChunk.length > 0) {
                innerHTML += '<span style="' + styleChunk + '">' + item + '</span>';
            }
            else {
                innerHTML += item;
            }
        }
        
        this.container.innerHTML = innerHTML;
    }
}

Object.extend(HtmlStyleAppender.prototype, StyleAppender.prototype);

var JsStyleAppender = Class.create();

JsStyleAppender.prototype = {

    processText: function(items, policies) {
    
        var style = this.containter.style;
        
        for (var i = 0; i < items.length; i++) {
        
            var item = items[i];
            
            if (item == "\n" || item == "<br />" || item == "<br/>") {
                //alert("Line break found");
                innerHTML += "<br />";
                continue;
            }
            
            var styleChunk = "";
            
            for (var j = 0; j < this.policies.length; j++) {
            
                var policy = this.policies[j];
                
                if (policy.isEmpty() || policy.isHomogenous()) {
                    continue;
                }
                
                styleChunk += this.appendStyle(policy);
            }
            
            if (styleChunk.length > 0) {
                innerHTML += '<span style="' + styleChunk + '">' + item + '</span>';
            }
            else {
                innerHTML += item;
            }
        }
        
        container.innerHTML = innerHTML;
    }
}

Object.extend(JsStyleAppender.prototype, StyleAppender.prototype);

/**********************************************************************
 *                            Splitter                                *
 **********************************************************************/
function TextSplitterByWord() {

}

TextSplitterByWord.prototype.split = function(text) {

    var result = [];
    var lines = text.split(/\n|<br *\/*>/i);
    /**/
     for(var i = 0; i < lines.length; i++) {
     var line = lines[i];
     var words = line.split(" ");
     for(var j = 0; j < words.length; j++) {
     result.push(words[j]);
     result.push(" ");
     }
     result.pop();
     result.push("<br />");
     }
     return result;
     }
     function TextSplitterByLetter() {
     }
     TextSplitterByLetter.prototype.split = function(text) {
     var result = [];
     var lines = text.split(/\n|<br *\/*>/i); /**/
    //alert(lines.length);
    
    for (var i = 0; i < lines.length; i++) {
    
        var line = lines[i];
        
        var letters = line.split('');
        
        for (var k = 0; k < letters.length; k++) {
        
            result.push(letters[k]);
            //if(letters[k] == "<") {
            //  alert("Found unescaped tag - check your code:\n" + line);
            //}
        
        }
        
        result.push("<br />");
    }
    
    return result;
    
}

function TextSplitterByEveryNLetter(every) {
    this.every = every;
}

TextSplitterByEveryNLetter.prototype.split = function(text) {

    var ret = [];
    
    var count = text.length / this.every;
    
    for (var i = 0; i < count; i++) {
        var fromText = i * this.every;
        var fromLetter = fromText + this.every;
        var untilText = fromLetter - 1;
        var untilLetter = fromLetter;
        ret.push(text.substring(fromText, untilText));
        ret.push(text.substring(fromLetter, untilLetter));
    }
}

var SplittingStrategy = {

    newStrategy: function(type) {
    
        if (type == "LETTER") {
            return new TextSplitterByLetter();
        }
        
        if (type == "WORD") {
            return new TextSplitterByWord();
        }
        
        if (type == "LETTERS") {
            return new TextSplitterByEveryNLetter();
        }
        
        throw new Error("No such strategy: " + type);
    }
};

