/**
 * @file Smarty4js
 * @author johnson [zoumiaojiang@gmail.com]
 * @date  2014-11-13
 */

var Compiler = require('./lib/compiler');
var Renderer = require('./lib/renderer');
var phpfunc = require('./lib/phpfunc');
var func = require('./lib/func');
var utils = require('./lib/utils');
var path = require('path');
var fs = require('fs');


/**
 * pretreatment template code
 *
 * @param  {string} code template code
 * @param  {Object} conf smarty config
 * @return {string}      code of after pre define
 */
function pretreatmentCode(code, conf) {
    var ld = conf.left_delimiter;
    var rd = conf.right_delimiter;
    var reld = utils.regEscape(ld);
    var rerd = utils.regEscape(rd);

    // literal filter
    var literalPaserModulePath = path.resolve(__dirname, 'lib', 'parser','literal.js');
    var literalPaserFile = fs.readFileSync(literalPaserModulePath, 'utf8');
    var homePath = utils.getHomePath();
    var realLiteralParserFilePath = path.resolve(homePath, 'literal.js');

    if (ld !== '{%') {
        literalPaserFile = literalPaserFile.replace(/\\\{%/g, reld);
    }
    if (rd !== '%}') {
        literalPaserFile = literalPaserFile.replace(/%\\\}/g, rerd);
    }

    fs.writeFileSync(realLiteralParserFilePath, literalPaserFile);

    var literalAst = require(realLiteralParserFilePath).parse(code);
    var newCode = '';

    literalAst.forEach(function (node) {
        var type = node.type;
        var value = node.value;
        if (value && value.trim().length > 0) {
            if (type === 'TEXT') {
                newCode += value;
            }
            else if (type === 'LTEXT') {
                newCode += value.replace(new RegExp(reld, 'g'), '__LD').replace(new RegExp(rerd, 'g'), '__RD');
            }
        }
    });

    // jison hack
    newCode += (ld + '*smarty4js*' + rd);

    return newCode
        .replace(/\\\"/g, '__QD') // replace \" in string ""
        .replace(/\\\'/g, '__QS'); // replace \' in string ''
}


/**
 * @constructor
 */
function Smarty() {
    this.id = '__smarty__' + utils.getGUID();
    this.phpfunc = phpfunc;
    this.func = func;
    this.conf = {
        'left_delimiter': '{%',
        'right_delimiter': '%}',
        'isAmd': true,
        'isCmd': true,
        'globalVar': '_smartyTpl'
    };
    this.config.apply(this, arguments);
    this.compiler = new Compiler(this);
    this.renderer = new Renderer(this);
}

/**
 * smarty config
 * @param  {Object} conf config options
 */
Smarty.prototype.config = function (conf) {
    this.conf = utils.extend(this.conf, conf);
};

/**
 * set tpl file base dir
 * @param {string} path  base path
 */
Smarty.prototype.setBasedir = function (path) {
    this.dirname = path;
}

/**
 * compile the smarty template code
 * @param  {string} tpl smarty template code/file path
 * @return {Object}     smarty Compiler object, have `render` mathod
 */
Smarty.prototype.compile = function (tpl) {

    var conf = this.conf;
    var filePath = path.resolve(process.cwd(), (tpl || '' + utils.getGUID()));
    var ld = conf.left_delimiter;
    var rd = conf.right_delimiter;
    var paserModulePath = path.resolve(__dirname, 'lib', 'parser','index.js');
    var paserFile = fs.readFileSync(paserModulePath, 'utf8');
    var homePath = utils.getHomePath();
    var realParserFilePath = path.resolve(homePath, 'index.js');

    if (fs.existsSync(filePath)) {
        tpl = fs.readFileSync(filePath, 'utf-8');
        this.dirname = this.dirname || path.dirname(filePath);
    }

    if (ld !== '{%') {
        paserFile = paserFile.replace(/\\\{%/g, utils.regEscape(ld));
    }
    if (rd !== '%}') {
        paserFile = paserFile.replace(/%\\\}/g, utils.regEscape(rd));
    }

    fs.writeFileSync(realParserFilePath, paserFile);
    this.ast = require(realParserFilePath).parse(pretreatmentCode(tpl, conf));

    return this.compiler;
};

/**
 * render tpl with json data
 * @param  {string} tpl  template code/file path
 * @param  {Object} data json data
 * @return {string}      html string
 */
Smarty.prototype.render = function (tpl, data) {

    var filePath = path.resolve(process.cwd(), tpl);

    if (fs.existsSync(filePath)) {
        tpl = fs.readFileSync(filePath, 'utf-8');
        this.dirname = this.dirname || path.dirname(filePath);
    }

    return this.compile(tpl).render(data);
};

/**
 * user-defined function regiester in smarty
 * @param  {Object}    funcObj  function Object
 */
Smarty.prototype.register = function (funcObj) {
    for (var p in funcObj) {
        if (funcObj.hasOwnProperty(p)) {
            this.func[p] = funcObj[p];
        }
    }
};

/**
 * php plugin regiester in smarty
 * @param  {Object}    plugins   plugin's Object
 */
Smarty.prototype.addPlugin = function (plugins) {
    for (var p in plugins) {
        if (plugins.hasOwnProperty(p)) {
            this.phpfunc[p] = plugins[p];
        }
    }
};

/**
 * support express
 *
 * @param  {string}   path    tpl path
 * @param  {Object}   options data
 * @param  {Function} fn      callback
 */
Smarty.__express = function (path, options, fn) {
    fn(null, new Smarty().render(path, options));
};

module.exports = Smarty;