/** @module jsdoc/tag/dictionary */
'use strict';

var definitions = require('jsdoc/tag/dictionary/definitions');

var hasOwnProp = Object.prototype.hasOwnProperty;

var dictionary;

/** @private */
function TagDefinition(dict, title, etc) {
    var self = this;
    etc = etc || {};

    this.title = dict.normalise(title);

    Object.defineProperty(this, '_dictionary', {
        value: dict
    });

    Object.keys(etc).forEach(function(p) {
        self[p] = etc[p];
    });
}

/** @private */
TagDefinition.prototype.synonym = function(synonymName) {
    this._dictionary.defineSynonym(this.title, synonymName);
    return this; // chainable
};

/**
 * @class
 * @alias module:jsdoc/tag/dictionary.Dictionary
 */
function Dictionary() {
    this._tags = {};
    this._tagSynonyms = {};
    // The longnames for `Package` objects include a `package` namespace. There's no `package` tag,
    // though, so we declare the namespace here.
    this._namespaces = ['package'];
}

/** @function */
Dictionary.prototype._defineNamespace = function defineNamespace(title) {
    title = this.normalise(title || '');

    if (title && this._namespaces.indexOf(title) === -1) {
        this._namespaces.push(title);
    }

    return this;
};

/** @function */
Dictionary.prototype.defineTag = function defineTag(title, opts) {
    var tagDef = new TagDefinition(this, title, opts);
    this._tags[tagDef.title] = tagDef;

    if (opts && opts.isNamespace) {
        this._defineNamespace(tagDef.title);
    }

    return this._tags[tagDef.title];
};

/** @function */
Dictionary.prototype.defineSynonym = function defineSynonym(title, synonym) {
    this._tagSynonyms[synonym.toLowerCase()] = this.normalise(title);
};

/** @function */
Dictionary.prototype.getNamespaces = function getNamespaces() {
    return this._namespaces.slice(0);
};

/** @function */
Dictionary.prototype.lookUp = function lookUp(title) {
    title = this.normalise(title);

    if ( hasOwnProp.call(this._tags, title) ) {
       return this._tags[title];
    }

    return false;
};

/** @function */
Dictionary.prototype.isNamespace = function isNamespace(kind) {
    if (kind) {
        kind = this.normalise(kind);
        if (this._namespaces.indexOf(kind) !== -1) {
            return true;
        }
    }

    return false;
};

/** @function */
Dictionary.prototype.normalise = function normalise(title) {
    var canonicalName = title.toLowerCase();

    if ( hasOwnProp.call(this._tagSynonyms, canonicalName) ) {
        return this._tagSynonyms[canonicalName];
    }

    return canonicalName;
};

/** @function */
Dictionary.prototype.normalize = Dictionary.prototype.normalise;

// initialize the default dictionary
dictionary = new Dictionary();
definitions.defineTags(dictionary);

// make the constructor available for unit-testing purposes
dictionary.Dictionary = Dictionary;

/** @type {module:jsdoc/tag/dictionary.Dictionary} */
module.exports = dictionary;
