package org.aegee.runanddine.pathfinder;

import java.io.Serializable;

import org.aegee.runanddine.util.model.Model;


/**
 * Model for persistent DistanceMatrices that contains information
 * about the distances between addresses in a RunAndDine
 */
public class DistanceMatrix extends Model {
    private static final long serialVersionUID = 1L;

    private DistanceMatrixEntry[][] entries;
    private int n, runAndDineId;

    /**
     * Set ID of RunAndDine the DistanceMatrix is related to
     * @param runAndDineId ID of related RunAndDine
     */
    public void setRunAndDineId(int runAndDineId) {
        this.runAndDineId = runAndDineId;
    }

    /**
     * Get ID of RunAndDine the DistanceMatrix is related to
     * @return Id of related RunAndDine
     */
    public int getRunAndDineId() {
        return runAndDineId;
    }

    public DistanceMatrix(int n) {
        entries = new DistanceMatrixEntry[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                entries[i][j] = new DistanceMatrixEntry();
            }
        }
    }

    public DistanceMatrix(DistanceMatrixEntry[][] entries) {
        this.entries = entries;
        this.n = entries.length;
    }

    public DistanceMatrixEntry[][] getEntries() {
        return entries;
    }

    /**
     * Set all entries of DistanceMatrix at once
     * @param entries Entries for the DistanceMatrix
     */
    public void setEntries(DistanceMatrixEntry[][] entries) {
        this.entries = entries;
    }

    @Override
    public String toString() {
        System.out.println("DistanceMatrix:");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                sb.append("[").append(entries[i][j].toString()).append("]");
            }
            sb.append("\n");
        }
        return sb.toString();
    }

    /**
     * Get single entry of DistanceMatrix
     * @param i Row number of entry
     * @param j Column number of entry
     * @return Entry in DistanceMatrix in row i and column j
     */
    public DistanceMatrixEntry getEntry(int i, int j) {
        return entries[i][j];
    }

    /**
     * Set single entry of DistanceMatrix
     * @param i Row number of entry
     * @param j Column number of entry
     * @param entry Entry to be set in row i and column j
     */
    public void setEntry(int i, int j, DistanceMatrixEntry entry) {
        System.out.println("Entry set:" + entry);
        this.entries[i][j] = entry;
    }

    /**
     * Get distance between two OptGroups
     * @param a First OptGroup
     * @param b Second OptGroup
     * @return Distance between first and second OptGroup
     */
    public double getDistance(OptGroup a, OptGroup b) {
        return getDistance(a.getId(), b.getId());
    }

    /**
     * Get distance between two OptGroups (via id)
     * @param id1 ID of first OptGroup
     * @param id2 ID of second OptGroup
     * @return Distance between first and second OptGroup
     */
    public double getDistance(int id1, int id2) {
        for (int i = 0; i < entries.length; i++) {
            if (entries[i][0].getId1() == id1) {
                for (int j = 0; j < entries.length; j++) {
                    if (entries[i][j].getId2() == id2) {
                        return Double.parseDouble(entries[i][j].getDistance().split(" ")[0].replace(",", "."));
                    }
                }
            }
        }
        return 0.0;
    }

    /**
     * Get duration to walk between two OptGroups
     * @param a First OptGroup
     * @param b Second OptGroup
     * @return Duration to walk between first and second OptGroup
     */
    public String getDuration(OptGroup a, OptGroup b) {
        return getDuration(a.getId(),b.getId());
    }

    /**
     * Get duration to walk between two OptGroups (via id)
     * @param id1 ID of first OptGroup
     * @param id2 ID of second OptGroup
     * @return Duration to walk between first and second OptGroup
     */
    public String getDuration(int id1, int id2) {
              for (int i = 0; i < entries.length; i++) {
            if (entries[i][0].getId1() == id1) {
                for (int j = 0; j < entries.length; j++) {
                    if (entries[i][j].getId2() == id2) {
                        return entries[i][j].getDuration();
                    }
                }
            }
        }
        return "not found";
    }
}

/**
 * Class for Entries of the DistanceMatrix
 */
class DistanceMatrixEntry implements Serializable {

    /**
     * ID of first OptGroup
     */
    private int id1;
    
    /**
     * ID of second OptGroup
     */
    private int id2;
    
    /**
     * Duration to walk between the two OptGroups
     */
    private String duration;
    
    /**
     * Distance between the two OptGroups
     */
    private String distance;

    public DistanceMatrixEntry() {
    }

    public DistanceMatrixEntry(int id1, int id2, String duration, String distance) {
        this.id1 = id1;
        this.id2 = id2;
        this.duration = duration;
        this.distance = distance;
    }

    /**
     * Get distance between the two OptGroups
     * @return Distance
     */
    public String getDistance() {
        return distance;
    }

    /**
     * Get duration to walk between the two OptGroups
     * @return Duration to walk
     */
    public String getDuration() {
        return duration;
    }

    /**
     * Get ID of first OptGroup
     * @return ID of first OptGroup
     */
    public int getId1() {
        return id1;
    }

    /**
     * Get ID of second OptGroup
     * @return ID of second OptGroup
     */
    public int getId2() {
        return id2;
    }

    /**
     * Set distance between the two OptGroups
     * @param distance Distance to be set
     */
    public void setDistance(String distance) {
        this.distance = distance;
    }

    /**
     * Set duration to walk between the two OptGroups
     * @param duration Duration to be set
     */
    public void setDuration(String duration) {
        this.duration = duration;
    }

    /**
     * Set ID of first OptGroup
     * @param id1 ID to be set
     */
    public void setId1(int id1) {
        this.id1 = id1;
    }

    /**
     * Set ID of second OptGroup
     * @param id2 ID to be set
     */
    public void setId2(int id2) {
        this.id2 = id2;
    }

    /**
     * Conversion to string for debugging
     * @return Debugging information
     */
    @Override
    public String toString() {
        return getId1() + " to " + getId2() + " time: " + getDuration() + " distance:" + getDistance();
    }
}