const java = require('java');
const fs = require("fs");
const URL = require('url');
const https = require('https');


function isPath(image)
{
    if (image.length < 256 && image.includes("/") || image.includes("\\"))
    {
        if (fs.existsSync(image))
        {
            return true;
        }
        throw new BarcodeException("Path " + image + " does not exist");
    }
    return false;
}


function isValidURL(str) {
    try {
        new URL(str);
        return true;
    } catch (err) {
        return false;
    }
}

function isBase64(str)
{
    try
    {
        return (Buffer.from(str, 'base64').toString('base64')) === str;
    }
    catch (err)
    {
        return false;
    }
}

function convertImageResourceToBase64(imageResource)
{
    if(imageResource === null)
    {
        return null;
    }
    else if ((typeof imageResource === 'string') && (isBase64(imageResource)))
    {
        return imageResource;
    }
    else if (fs.existsSync(imageResource))
    {
        return fs.readFileSync(imageResource).toString('base64');
    }
    throw new BarcodeException("Image is not available or this resouce is not supported!");
}

function convertDecodeTypeToFormattedDecodeType(decodeTypes)
{
    if (decodeTypes === undefined || decodeTypes == null)
    {
        return [DecodeType.ALL_SUPPORTED_TYPES];
    }
    else
    {
        if(!Array.isArray(decodeTypes))
            decodeTypes = [decodeTypes];

        for (let i = 0; i < decodeTypes.length; i++)
        {
            if(!Number.isInteger(decodeTypes[i]))
                throw new Error("Unsuported decodeType format");
        }
        return decodeTypes;
    }
}

function convertAreasToStringFormattedAreas(areas)  // TODO move to Joint
{
    let stringFormattedAreas = null;
    if (areas != null)
    {
        stringFormattedAreas = [];
        if(Array.isArray(areas))
        {
            if(!areas.every(area => area === null))
            {
                for(let i = 0; i < areas.length; i++)
    {
                    let area = areas[i];
                    if (area === null || !(area instanceof Rectangle))
                        throw new BarcodeException('All elements of areas should be instances of Rectangle class');
                    stringFormattedAreas.push(area.toString());
                }
            }
    }
    else
    {
            if (!(areas instanceof Rectangle))
                throw new BarcodeException('All elements of areas should be instances of Rectangle class');
            stringFormattedAreas.push(areas.toString());
        }
        stringFormattedAreas = stringFormattedAreas.length > 0 ? stringFormattedAreas : null
    }
    return stringFormattedAreas;
}

class BaseJavaClass
{
    javaClass;
    javaClassName;

    constructor(javaClass)
    {
        this.javaClass = javaClass;

        if (this.javaClassName == null || this.javaClassName === "")
        {
            this.javaClassName = this.javaClass.__signature;
        }
    }

    init()
    {
        throw new BarcodeException('You have to implement the method init!');
    }

    /**
     * @return mixed
     */
    getJavaClass()
    {
        return this.javaClass;
    }

    setJavaClass(javaClass)
    {
        this.javaClass = javaClass;
        this.init();
    }

    getJavaClassName()
    {
        return this.javaClassName;
    }

    isNull()
    {
        return java_cast(this.javaClass.isNull(), "boolean");
    }

    printJavaClassName()
    {
        console.log("Java class name => '" + this.javaClassName + "'");
    }
}

/**
 * Provides methods to license the component.
 */
class License extends BaseJavaClass
{
    static get javaClassName()
    {
        return "com.aspose.nodejs.barcode.license.NodejsLicense"
    };

    /**
     * Initializes a new instance of this class.
     */
    constructor()
    {
        let javaLicense = java.import(License.javaClassName);
        super(new javaLicense());
    }

    /**
     * Licenses the component.
     *
     * @param licensePath path to license file
     */
    setLicense(licensePath)
    {
        try
        {
            let file_data = License.openFile(licensePath);
            this.getJavaClass().setLicenseSync(file_data);
        } catch (ex)
        {
            throw new BarcodeException(ex);
        }
    }

    resetLicense()
    {
        try
        {
            let javaClass = this.getJavaClass();
            javaClass.resetLicenseSync();
        } catch (ex)
        {
            throw new BarcodeException(ex);
        }
    }

    isLicensed()
    {
        let is_licensed = this.getJavaClass().isLicensedSync();
        return is_licensed.toString();
    }

    static openFile(filename)
    {
        let buffer = Buffer.from(fs.readFileSync(filename, 'utf8'));
        let array = [];
        array.push('');
        for (let i = 0; i < buffer.length; i++)
        {
            array.push(buffer[i] + '');
        }
        return array;
    }

    init()
    {
//      do nothing
    }
}

/**
 * Class BarcodeException
 */
class BarcodeException extends Error
{
    static get MAX_LINES()
    {
        return 4;
    };

    /**
     * BarcodeException constructor.
     * @param  exc exception's instance
     */
    constructor(exc)
    {
        super();
        if ((typeof exc.toString()) === 'string')
        {
            this.setMessage(exc.toString());
            return;
        }
        let exc_message = "Exception occured in file:line" + nl;

        this.setMessage(exc_message);
    }

    getDetails(exc)
    {
        let details = "";
        if (typeof exc === 'string' || exc instanceof String)
        {
            return exc;
        }
        if (get_class(exc) != null)
        {
            details = "exception type : " + get_class(exc) + "\n";
        }
        if (method_exists(exc, "__toString"))
        {
            details += exc.__toString();
        }
        if (method_exists(exc, "getMessage"))
        {
            details += exc.getMessage();
        }
        if (method_exists(exc, "getCause"))
        {
            details += exc.getCause();
        }
        return details;
    }

    /**
     * @param mixed message
     */
    setMessage(message)
    {
        this.message = message;
    }

}

class Rectangle extends BaseJavaClass
{

    /**
     * @return {Rectangle} empty Rectangle
     */
    static get EMPTY()
    {
        return new Rectangle(0, 0,0,0);
    }

    init()
    {
    }

    static get javaClassName()
    {
        return "java.awt.Rectangle";
    }

    /**
     * Rectangle constructor.
     * @param x
     * @param y
     * @param width
     * @param height
     */
    constructor(x, y, width, height)
    {
        let java_class_link = java.import(Rectangle.javaClassName);
        let java_class = new java_class_link(x, y, width, height);
        super(java_class);
    }

    static construct(...args)
    {
        let rectangle = this.EMPTY;
        rectangle.setJavaClass(args[0]);
        return rectangle;
    }

    /**
     * @return X
     */
    getX()
    {
        return this.getJavaClass().getXSync();
    }

    /**
     * @return Y
     */
    getY()
    {
        return this.getJavaClass().getYSync();
    }

    /**
     * @return Left
     */
    getLeft()
    {
        return this.getX();
    }

    /**
     * @return Top
     */
    getTop()
    {
        return this.getY();
    }

    /**
     * @return Right
     */
    getRight()
    {
        return this.getX() + this.getWidth();
    }

    /**
     * @return Bottom
     */
    getBottom()
    {
        return this.getY() + this.getHeight();
    }

    /**
     * @return Width
     */
    getWidth()
    {
        return this.getJavaClass().getWidthSync();
    }

    /**
     * @return Height
     */
    getHeight()
    {
        return this.getJavaClass().getHeightSync();
    }

    toString()
    {
        return this.getX() + ',' + this.getY() + ',' + this.getWidth() + ',' + this.getHeight();
    }

    equals(obj)
    {
        return this.getJavaClass().equalsSync(obj.getJavaClass());
    }

    /**
     * Determines if this rectangle intersects with rect.
     * @param rectangle
     * @returns {boolean}
     */
    intersectsWithInclusive(rectangle)
    {
        return !((this.getLeft() > rectangle.getRight()) || (this.getRight() < rectangle.getLeft()) ||
            (this.getTop() > rectangle.getBottom()) || (this.getBottom() < rectangle.getTop()));
    }

    /**
     * Intersect Shared Method
     * Produces a new Rectangle by intersecting 2 existing
     * Rectangles. Returns null if there is no  intersection.
     */
    static intersect(a, b)
    {
        if (!a.intersectsWithInclusive(b))
        {
            return new Rectangle(0, 0, 0, 0);
        }
        return Rectangle.fromLTRB(Math.max(a.getLeft(), b.getLeft()),
            Math.max(a.getTop(), b.getTop()),
            Math.min(a.getRight(), b.getRight()),
            Math.min(a.getBottom(), b.getBottom()));
    }

    /**
     * FromLTRB Shared Method
     * Produces a Rectangle class from left, top, right,
     * and bottom coordinates.
     */
    static fromLTRB(left, top, right, bottom)
    {
        return new Rectangle(left, top, right - left, bottom - top);
    }

    isEmpty()
    {
        return (this.getWidth() <= 0) || (this.getHeight() <= 0);
    }
}

/**
 * Represents Point class
 */
class Point extends BaseJavaClass
{
    static get javaClassName()
    {
        return "java.awt.Point";
    }

    /**
     * Represents an empty Point
     */
    static get EMPTY()
    {
        return new Point(0, 0);
    }

    /**
     * Point constructor.
     * @param x
     * @param y
     */
    constructor(x, y)
    {
        let java_class_link = java.import(Point.javaClassName);
        let java_class = new java_class_link(x, y);
        super(java_class);
    }

    static construct(...args)
    {
        let point = Point.EMPTY;
        point.setJavaClass(args[0]);
        return point;
    }

    init()
    {
    }

    /**
     * @return X
     */
    getX()
    {
        return this.getJavaClass().getXSync();
    }

    /**
     * @return Y
     */
    getY()
    {
        return this.getJavaClass().getYSync();
    }

    /**
     * @param x X
     */
    setX(x)
    {
        this.getJavaClass().xSync = x;
    }

    /**
     * @param y Y
     */
    setY(y)
    {
        this.getJavaClass().ySync = y;
    }

    toString()
    {
        return this.getX() + ',' + this.getY();
    }

    equals(obj)
    {
        return this.getJavaClass().equalsSync(obj.getJavaClass());
    }
}

class BuildVersionInfo
{
    static get javaClassName()
    {
        return "com.aspose.barcode.BuildVersionInfo";
    }

    static get javaClass()
    {
        let java_class_link = java.import(BuildVersionInfo.javaClassName);
        return java_class_link;
    }

    static get product()
    {
        return BuildVersionInfo.javaClass.PRODUCT;
    }

    static get assemblyVersion()
    {
        return BuildVersionInfo.javaClass.ASSEMBLY_VERSION;
    }

}

module.exports = {
    BaseJavaClass, BarcodeException, Rectangle, Point, License, BuildVersionInfo, isPath, convertImageResourceToBase64,
    convertDecodeTypeToFormattedDecodeType, convertAreasToStringFormattedAreas
};