package classes;

import com.google.gson.annotations.Expose;

import java.awt.*;
import java.util.LinkedList;

/**
 * The class "HolonElement" represents any possible element that can be added to
 * a CpsObject (such as TV (consumer) or any energyPerElement source/producer).
 *
 * @author Gruppe14
 */
public class HolonElement {

    /* Points on the UnitGraph */
    private LinkedList<Point> graphPoints;
    /* Name of the gadget */
    @Expose
    private String eleName;
    /* Quantity */
    @Expose
    private int amount;
    /* Currently used energy per element (- indicates consumation of energy, + indicates production of energy)*/
    @Expose
    private float energyPerElement;
    /* Whether the gadget is active or not (currently uses/produces the energy in energyPerElement) */
    @Expose
    private boolean active;
    /* Gives us whether this element is flexible and can flexibly use any part of the energy in flexibleEnergyAvailable */
    @Expose
    private boolean flexible;
    /* Flexibility (meaning the actual */
    @Expose
    private float flexibleEnergyAvailable;
    /* -: for Consumers and +: Producers */
    @Expose
    private char sign;
    /* Place where the Object is Stored */
    @Expose
    private Pair<String, String> saving;
    /* ID */
    @Expose
    private int id;
    /*
     * Energy at each point of the graph with 100 predefined points. At the
     * beginning, it starts with all values at energyPerElement.
     * If switched to flexible, this represents the maximum of usable energy
     */
    private float[] availableEnergyPerElementAt;

    /**
     * Create a new HolonElement with a user-defined name, amount of the same
     * element and energyPerElement.
     *
     * @param eleName String
     * @param amount  int
     * @param energy  float
     */
    public HolonElement(String eleName, int amount, float energy) {
        setEleName(eleName);
        setAmount(amount);
        setEnergyPerElement(energy);
        setActive(true);
        setSign(energy);
        setAvailableEnergyPerElementAt(energy);
        setGraphPoints(new LinkedList<>());
        setId(IdCounterElem.nextId());
        setFlexibleEnergyAvailable(0);
        setFlexible(false);
    }

    /**
     * same as standard constructor, but with already given id (so the counter is not increased twice)
     */
    public HolonElement(String eleName, int amount, float energy, int id) {
        setEleName(eleName);
        setAmount(amount);
        setEnergyPerElement(energy);
        setActive(true);
        setSign(energy);
        setAvailableEnergyPerElementAt(energy);
        setGraphPoints(new LinkedList<>());
        setId(id);
        setFlexibleEnergyAvailable(0);
        setFlexible(false);
    }

    /**
     * Create a copy of the HolonElement given each one a new ID.
     *
     * @param element element to copy
     */
    public HolonElement(HolonElement element) {
        setEleName(element.getEleName());
        setAmount(element.getAmount());
        setEnergyPerElement(element.getEnergyPerElement());
        setActive(element.isActive());
        setSign(element.getEnergyPerElement());
        setAvailableEnergyPerElementAt(element.getEnergyPerElement());
        for (int i = 0; i < availableEnergyPerElementAt.length; i++) {
            availableEnergyPerElementAt[i] = element.getAvailableEnergyPerElementAt()[i];
        }
        setGraphPoints(new LinkedList<>());
        for (Point p : element.getGraphPoints()) {
            this.graphPoints.add(new Point((int) p.getX(), (int) p.getY()));
        }
        setSaving(null);
        setId(IdCounterElem.nextId());
        setFlexibleEnergyAvailable(0);
        setFlexible(false);
    }

    /**
     * Get the Array of energyPerElement (100 values).
     *
     * @return availableEnergyPerElementAt Array of Floats
     */
    public float[] getAvailableEnergyPerElementAt() {
        return availableEnergyPerElementAt;
    }

    /**
     * Set energyPerElement to any value to the whole array.
     *
     * @param energy the value
     */
    public void setAvailableEnergyPerElementAt(float energy) {
        this.availableEnergyPerElementAt = new float[100];
        for (int i = 0; i < availableEnergyPerElementAt.length; i++) {
            this.availableEnergyPerElementAt[i] = energy;
        }
    }

    /**
     * Get the energyPerElement currently available
     */
    public float getAvailableEnergyAt(int timestep) {
        return this.availableEnergyPerElementAt[timestep];
    }

    /**
     * Set energyPerElement to any value at a given position.
     *
     * @param pos    int
     * @param energyPerElement float
     */
    public void setAvailableEnergyPerElementAt(int pos, float energyPerElement) {
        this.availableEnergyPerElementAt[pos] = energyPerElement;
    }

    /**
     * Get the user-defined Name.
     *
     * @return the name String
     */
    public String getEleName() {
        return eleName;
    }

    /**
     * Set the name to any new name.
     *
     * @param name the name to set
     */
    public void setEleName(String name) {
        this.eleName = name;
    }

    /**
     * Get the actual amount of Elements in the selected Object.
     *
     * @return the amount int
     */
    public int getAmount() {
        return amount;
    }

    /**
     * Set the amount of the Element in the selected Object.
     *
     * @param amount the amount to set
     */
    public void setAmount(int amount) {
        this.amount = amount;
    }

    /**
     * Get the energyPerElement value of the selected Element.
     *
     * @return the energyPerElement
     */
    public float getEnergyPerElement() {
        return energyPerElement;
    }

    /**
     * Set the energyPerElement value of the selected Element.
     *
     * @param energyPerElement the energyPerElement to set
     */
    public void setEnergyPerElement(float energyPerElement) {
        this.energyPerElement = energyPerElement;
        setSign(energyPerElement);
    }

    /**
     * Get the Status of the Element (see description of variables).
     *
     * @return the active
     */
    public boolean isActive() {
        return active;
    }

    /**
     * Set the Status of the Element (see description of variables).
     *
     * @param active the active to set
     */
    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     * Multiply the amount of gadgets, given by the user, and the
     * consumption/production. If the switch isWorking is turned off for on
     * gadget, the energyPerElement of this gadget have to be subtracted.
     *
     * @return totalEnergy (actual)
     */
    public float getOverallEnergy() {
        float totalEnergy = ((float) amount) * energyPerElement;
        return totalEnergy;
    }

    /**
     * Get the energyPerElement value at a selected time x.
     *
     * @param x int
     * @return energyPerElement value
     */
    public float getOverallEnergyAtTimeStep(int x) {
        if (flexible) {
            return ((float) amount) * energyPerElement;
        } else {
            return ((float) amount) * availableEnergyPerElementAt[x];
        }
    }

    /**
     * Get the symbol of the value (see variable description).
     *
     * @return the sign
     */
    public char getSign() {
        return sign;
    }

    /**
     * Set symbol of the value.
     *
     * @param energy the sign to set
     */
    public void setSign(float energy) {
        if (energy < 0)
            this.sign = '-';
        else
            this.sign = '+';
    }

    /**
     * Get the points (values) in the graph.
     *
     * @return the Graph Points
     */
    public LinkedList<Point> getGraphPoints() {
        return graphPoints;
    }

    /**
     * Set the points (values) in the graph.
     *
     * @param points the Graph points
     */
    public void setGraphPoints(LinkedList<Point> points) {
        this.graphPoints = points;
    }

    /**
     * Get the flexibleEnergyAvailable of an element
     */
    public float getFlexibleEnergyAvailablePerElement() {
        return this.flexibleEnergyAvailable;
    }

    /**
     * Set the flexibleEnergyAvailable of an element
     */
    public void setFlexibleEnergyAvailable(float f) {
        this.flexibleEnergyAvailable = f;
    }

    /**
     * Get the flexibleEnergyAvailable of an element
     */
    public boolean isFlexible() {
        return this.flexible;
    }

    /**
     * Set the flexibleEnergyAvailable of an element, ~switches energyPerElement and flexible energyPerElement
     */
    public void setFlexible(boolean b) {
        // if flexibleEnergyAvailable was set to true
        if (b && !this.flexible) {
            this.flexible = b;
            // move energyPerElement to flexibleEnergyAvailable (becomes the possible-to-use energyPerElement)
            if (getEnergyPerElement() != 0) {
                setFlexibleEnergyAvailable(getEnergyPerElement());
                setEnergyPerElement(0);
            }
        } else if (!b && this.flexible) {
            this.flexible = b;
            // move the energyPerElement to actually used energyPerElement and set flexible amount to 0
            if (getFlexibleEnergyAvailablePerElement() != 0) {
                setEnergyPerElement(getFlexibleEnergyAvailablePerElement());
            }
            setFlexibleEnergyAvailable(0);
        }
    }

    /**
     * Get the Id of the selected HolonElement.
     *
     * @return id the id
     */
    public int getId() {
        return id;
    }

    /**
     * Set the ID of the HolonElement (one time only).
     *
     * @param id the id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the saving
     */
    public Pair<String, String> getSaving() {
        return saving;
    }

    /**
     * @param saving the saving to set
     */
    public void setSaving(Pair<String, String> saving) {
        this.saving = saving;
    }


    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[HolonElement: ");
        sb.append("id=").append(id)
                .append(", eleName=").append(eleName)
                .append(", amount=").append(amount)
                .append(", active=").append(active)
                .append(", flexible=").append(flexible)
                .append(", energyPerElement used=").append(energyPerElement)
                .append(", flexible energyPerElement available=").append(flexibleEnergyAvailable);
        sb.append("]");

        return sb.toString();
    }
}
