'use strict';
const fs         = require('fs-extra');
const path       = require('path');
const merge      = require('merge');
const hogan      = require('hogan.js');
const parallel   = require('run-parallel');
const auto       = require('run-auto');
const glob_copy  = require('glob-copy');
const jsonfile   = require('jsonfile');
const glob       = require("glob");

const Events     = require('./events');
const section    = require('./section');
const file       = require('./file');
const language   = require('./language');
class Project extends Events {
  
  constructor(options) {

    super();
    this.filenames = [];
    this.files = [];
    this.options = merge({
      name: null,
      files: [],
      ignore: [],
      owners: 'contributors',
      output: 'docs',
      readme: null,
      version: null,
      links: {},
      languages: {}
    }, options);

    this.ignored = [];
    this.templates = {};
    this.templates_ready = false;

    this.default_template_data = {
      project: this.options.name,
      title: this.options.name,
      path: '',
      classes: '',
      year: new Date().getUTCFullYear(),
      owners: this.options.owners,
      generate_time: new Date().toUTCString(),
      links: this.options.links
    };
    this.prepare_templates();
  }
  prepare_template(filename, callback) {

    fs.readFile(filename, 'utf-8', function(err, data){
      
      if(err) {
        callback(err, data);
        return;
      }
      
      var template = hogan.compile(data);

      callback(err, template);
      
    });
  }

  get_file(filename) {
    
    for(var i=0; i<this.files.length; i++) {
      if(this.files[i].filename == filename) return this.files[i];
    }
    
    return null;
  }
  prepare_templates() {
    var i,
        templates = [
          ['page', 'page.html'],
          ['file', 'file.html'],
          ['link', 'link.html'],
          ['section', 'section.html']
        ];
    var complete = 0,
        total = templates.length;

    var scope = this;

    for(i=0; i<templates.length; i++) {
      (function(name, filename) {
        var true_filename = path.join(__dirname, 'template', filename);
        
        scope.prepare_template.call(scope, true_filename, function(err, template) {
          complete += 1;

          if(err) {
            scope.emit.call(scope, 'template-err', { filename: true_filename });
          } else {
            scope.templates[name] = template;
            scope.emit.call(scope, 'template-ready', { template: template });
          }
          
          if(complete == total) {
            scope.templates_ready = true;
            scope.emit.call(scope, 'templates-ready');
          }
          
        });
      })(templates[i][0], templates[i][1]);
      
    }
  }
  add_files(files) {
    
    var i;
    for(i=0; i<files.length; i++) {
      
      if(this.filenames.indexOf(files[i]) < 0) {
        this.filenames.push(files[i]);
      } else if(files[i].startsWith(this.options.output)) {
        this.emit('recursive-file', { filename: files[i] });
      }
      
    }

  }
  glob_files() {
    var filenames = [], i;

    for(i=0; i<this.filenames.length; i++) {
      filenames.push.apply(filenames, glob.sync(this.filenames[i]));
    }

    this.filenames = filenames;
  }
  glob_ignore(ignore) {
    var filenames = [], i;

    for(i=0; i<ignore.length; i++) {
      filenames.push.apply(filenames, glob.sync(ignore[i]));
    }

    this.ignored = filenames;
  }
  sort_files() {
    function id(a) {
      var parsed = path.parse(a);
      return parsed.dir + parsed.ext + parsed.name;
    }
    
    this.filenames.sort(function(a, b) {
      if(id(a) < id(b)) return -1;
      if(id(a) > id(b)) return 1;
      return 0;
    });
  }

  remove_duplicate_files() {
    var filenames = [];

    for(var i=0; i<this.filenames.length; i++) {
      if(filenames.indexOf(this.filenames[i]) >= 0) {
        if(this.filenames[i] == this.options.readme) continue;
        this.emit('duplicate-file', { filename: this.filenames[i] });
      } else {
        filenames.push(this.filenames[i]);
      }
    }

    this.filenames = filenames;
  }
  
  remove_ignored_files() {
    var filenames = [], i;

    for(i=0; i<this.filenames.length; i++) {
      if(this.ignored.indexOf(this.filenames[i]) >= 0) {
        continue;
      } else {
        filenames.push(this.filenames[i]);
      }
    }

    this.filenames = filenames;
  }
  copy(files, callback) {
    if(!Array.isArray(files)) files = [files];
    var total = files.length,
        complete = 0,
        scope = this;
    
    function copy_single_glob(filename, cb) {

      var src = path.join(__dirname, 'public', filename),
          dest = path.join(scope.options.output, 'public');
      fs.mkdirs(dest, function (err) {
        glob_copy(src, dest, function(err, results) {
          cb(null, null);
        });
      });
    }
    
    for(var i=0; i<files.length; i++) {
      
      copy_single_glob(files[i], function() {
        complete += 1;
        if(complete == total)
          callback.apply(callback, arguments);
      });
      
    }
  }
  
  copy_css(callback) {
    this.copy('style.css', callback);
  }
  
  copy_images(callback) {
    this.copy('*.png', callback);
  }
  
  copy_js(callback) {
    this.copy('*.js', callback);
  }
  
  copy_fonts(callback) {
    this.copy(['*.eot', '*.woff'], callback);
  }
  copy_public(callback) {
    var scope = this;
    
    parallel([
      
      function(next) {
        scope.copy_js.call(scope, next);
      },

      function(next) {
        scope.copy_css.call(scope, next);
      },
      
      function(next) {
        scope.copy_fonts.call(scope, next);
      },
      
      function(next) {
        scope.copy_images.call(scope, next);
      }
    ], callback);
    
  }

  each_file(func, callback) {
    
    var scope = this,
        total = this.files.length,
        complete = 0,
        errors = 0;
    
    var i;
    function run_complete() {
      callback({
        total: total,
        complete: complete,
        errors: errors
      });
    }
    function file_complete(err, data) {
      if(err) errors += 1;
      complete += 1;
      if(total == complete) run_complete();
    };
    for(i=0; i<this.files.length; i++) {
      func(this.files[i], file_complete);
    }

  }
  run_files(callback) {

    var i, f;

    if(this.options.readme) {
      this.add_files([this.options.readme]);
    }
    
    this.glob_ignore(this.options.ignore);
    
    this.add_files(this.options.files);

    this.glob_files();
    
    this.remove_duplicate_files();
    this.remove_ignored_files();
    
    this.sort_files();

    var scope = this,
        total = this.filenames.length + 1,
        complete = 1,
        errors = 0;
    
    if(this.filenames.length == 0) {
      console.log('! no files to parse');
      return false;
    }

    for(i=0; i<this.filenames.length; i++) {
      f = new file.File(this, this.filenames[i]);
      this.files.push(f);
    }

    if(this.options.readme) {
      f = new file.File(this, this.options.readme);
      f.index = true;
      f.path = '';
      this.files.push(f);
    } else {
      f = new file.File(this, 'README.default.md', [
        '# No `README` was provided',
        '(hint: use the `readme` option in the JSON configuration file to set your own `README` file)'
      ].join('\n'));
      
      f.index = true;
      f.path = '';
      this.files.push(f);
    }

    this.each_file(function(file, callback) {
      
      file.run(function(err, data) {
        scope.emit.call(scope, 'run-file-complete', { err: err, data: data });
        callback(err, data);
      });
      
    }, function(data) {
      scope.emit.call(scope, 'run-complete', data);

      if(callback) {
        callback(null, data);
      }
    });

    return true;
    
  }
  get_link_filenames(file) {

    var i, filenames=[], last_path_dir;
    
    for(i=0; i<this.files.length; i++) {
      if(this.files[i] === file) continue;

      if(this.files[i].filename === this.options.readme) continue;
      
      if(this.files[i].index) continue;
      
      var lang = language.get_language(path.extname(this.files[i].filename).substr(1), {});
      if(lang[1] === 'auto') lang = null;
      else                  lang = lang[0];
      
      filenames.push({
        filename: this.files[i].filename,
        link: path.relative(file.get_link(), this.files[i].get_link()),
        separator: ((path.dirname(this.files[i].filename) != last_path_dir) && filenames.length ? true : false),
        language: (lang ? lang.name.abbr : null),
        color: (lang ? lang.color : '#f0f')
      });

      last_path_dir = path.dirname(this.files[i].filename);
      
    }

    return filenames;
  }
  _run(callback) {

    var scope = this;
    
    function r() {
      
      auto({
        
        files: function(next) {
          scope.run_files.call(scope, function(err, data) {
            next(err, data);
          });
        },
        
        copy: function(next) {
          scope.copy_public.call(scope, function(err, data) {
            next(err, data);
          });
        }
        
      }, function(err, results) {
        callback(err, results.files);
      });
    }

    if(this.options.version == 'package.json') {
      
      jsonfile.readFile('package.json', function(err, data) {
        
        if(err || !('version' in data)) {
          scope.emit.call('package-json-err');
          return;
        }
        
        scope.options.version = data.version;
        
        r();
        
      });
      
    } else {
      r();
    }
             
  }
  run(callback) {
    
    if(this.templates_ready) {
      
      this._run(callback);
      
    } else {
      
      var scope = this;
      
      this.on('templates-ready', function() {
        scope._run.call(scope, callback);
      });
      
    }
    
  }
  
}

exports.Project = Project;

exports.project = {
  create: function(options) {
    return new Project(options);
  }
};