/**
 * @module Image
 */

import Book from './book';

/**
 * Image object from API.
 * @global
 * @typedef {object} APIImage
 * @property {string}        t Image type.
 * @property {number|string} w Image width.
 * @property {number|string} h Image height.
 */

/**
 * @typedef {object} ImageTypes
 * @property {TagType} JPEG JPEG image type.
 * @property {TagType} PNG  PNG image type.
 */

/**
 * Class representing image type.
 * @class
 */
class ImageType {
	/**
	 * @type {ImageTypes}
	 * @static
	 */
	static knownTypes = {};

	/**
	 * Image type name.
	 * @type {?string}
	 */
	type = null;

	/**
	 * Image type extension.
	 * @type {?string}
	 */
	extension = null;

	/**
	 * Create image type.
	 * @param {string} type      Image type name.
	 * @param {string} extension Image type extension.
	 */
	constructor(type, extension) {
		if (type) {
			this.type = type;
			this.constructor.knownTypes[type] = this;
		}
		this.extension = extension;
	}

	/**
	 * Whatever this tag type is unknown.
	 * @type {boolean}
	 */
	get isKnown() {
		return !(this instanceof UnknownImageType);
	}
}

/**
 * Class representing unknown image type.
 * @class
 * @extends ImageType
 */
class UnknownImageType extends ImageType {
	/**
	 * Create unknown image type.
	 * @param {string} type      Unknown image type name.
	 * @param {string} extension Unknown image type extension.
	 */
	constructor(type, extension) {
		super(null, extension);
		this.type = type;
	}
}

/**
 * Class representing image.
 * @class
 */
class Image {
	/**
	 * Image types.
	 * @type {ImageTypes}
	 * @static
	 */
	static types = {
		JPEG: new ImageType('jpeg', 'jpg'),
		PNG : new ImageType('png', 'png'),

		/**
		 * Known image types.
		 * @type {ImageType}
		 */
		known: ImageType.knownTypes,

		/**
		 * Get image type class instance by name.
		 * @param {string} type Image type.
		 * @returns {ImageType|UnknownImageType} Image type class instance.
		 */
		get(type) {
			let known;
			if ('string' === typeof type) {
				type = type.toLowerCase();
				switch (type) {
					case 'j':
					case 'jpg':
					case 'jpeg':
						type = 'jpeg';
						break;
					case 'p':
					case 'png':
						type = 'png';
						break;
				}
			}
			return ((known = this.known[type])) ? known : new UnknownImageType(type);
		},
	};

	/**
	 * Parse pure image object from API into class instance.
	 * @param {APIImage} image  Image object
	 * @param {number}   [id=0] Image id (a.k.a. page number).
	 * @returns {Image} Image instance.
	 * @static
	 */
	static parse(image, id = 0) {
		let {
			t: type,
			w: width,
			h: height,
		} = image;

		return new this({
			type,
			width : +width,
			height: +height,
			id,
		});
	}

	/**
	 * Image ID.
	 * @type {number}
	 */
	id = 0;

	/**
	 * Image width.
	 * @type {number}
	 */
	width = 0;

	/**
	 * Image height.
	 * @type {number}
	 */
	height = 0;

	/**
	 * Image type.
	 * @type {ImageType}
	 */
	type = this.constructor.types.JPEG;

	/**
	 * Image parent book.
	 * @type {Book}
	 */
	book = Book.Unknown;

	/**
	 * Create image.
	 * @param {object}           [params]                      Image parameters.
	 * @param {number}           [params.id=0]                 Image ID.
	 * @param {number}           [params.width=0]              Image width.
	 * @param {number}           [params.height=0]             Image height.
	 * @param {string|ImageType} [params.type=ImageTypes.JPEG] Image type.
	 * @param {Book}             [params.book=Book.Unknown]    Image's Book.
	 */
	constructor({
		id     = 0,
		width  = 0,
		height = 0,
		type   = this.constructor.types.JPEG,
		book   = Book.Unknown,
	} = {}) {
		Object.assign(this, {
			id: 'number' === typeof id
				? id < 1 ? 0 : id
				: 0,
			width,
			height,
			type: type instanceof ImageType
				? type
				: this.constructor.types.get(type),
			book: book instanceof Book
				? book
				: Book.Unknown,
		});
	}

	/**
	 * Whatever this image is book cover.
	 * @type {boolean}
	 */
	get isCover() {
		return this.id < 1;
	}

	/**
	 * Image filename.
	 * @type {string}
	 */
	get filename() {
		return `${this.isCover ? 'cover' : this.id}.${this.type.extension}`;
	}
}

export default Image;