# -*- coding: utf-8 -*-
"""
Created on Sun Dec  6 14:47:32 2020

@author: user
"""

import random as rd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
rd.seed(3)


truckCapacity = 250
depotMaxSupply = 500
truckFixedCost = 500
class Node(object):
    def __init__(self, nid, x, y, demand, supply, openCost):
        self.nid = nid
        self.x = x
        self.y = y
        self.demand = demand
        self.supply = supply
        self.openCost = openCost
        
    def __str__(self):
        return f"nid={self.nid}, x={self.x}, y={self.y}, demand={self.demand}, supply={self.supply}, open cost={self.openCost}"
    __repr__=__str__
    
class Arc(object):
    def __init__(self, aid=None, na=None, nb=None):
        self.aid = aid
        self.na = na
        self.nb = nb
        self.length = self.__length()
    
    def __length(self):
        return ((self.na.x-self.nb.x)**2+(self.na.y-self.nb.y)**2)**0.5
    
    def __str__(self):
        return f"aid = {self.aid}\n{self.na}\n{self.nb}\nlength={self.length}\n"
    
    __repr__ = __str__

class Graph(object):
    def __init__(self, route1:list, route2:list, route3:list):
        self.nodes = [Node(i, rd.randint(0,100), rd.randint(0,100), rd.randint(20,32), depotMaxSupply, rd.randint(800, 1200)) for i in range(60)]
        
        self.route1 = route1
        self.route2 = route2
        self.route3 = route3
        
        for i in range(len(self.nodes)):
            if i < 10:
                self.nodes[i].supply = depotMaxSupply
                self.nodes[i].demand = 0
            else:
                self.nodes[i].supply = 0
                self.nodes[i].openCost = 0
        
        with open("LRPnodes.txt", 'w') as f:
            for n in self.nodes:
                f.write(f"{n}\n")
        
        
        self.totalLoad = 0        
        self.arcs = []
        for a in self.nodes:
            row = [Arc(aid=(a.nid, b.nid), na = a, nb = b) for b in self.nodes]
            self.arcs.append(row) 
            self.totalLoad+=a.demand
        
        print(f"客戶總荷重為{self.totalLoad}")
        
        
        ## file of arcs
        with open("LRParcs.txt", 'w') as f:
            for row in self.arcs:
                for arc in row:
                    f.write(f"{arc}\n")
                f.write(f"\n\n")
        
        ## excel for arc length
        df = pd.DataFrame(np.zeros((len(self.nodes), len(self.nodes))))
        for rid, row in enumerate(self.arcs):
            for cid, arc in enumerate(row):
                df.iloc[rid, cid] = arc.length
        df.to_csv('LRPArcLength.xls')
        
        routes = route1+route2+route3 ##[0, 1,2,3,..., 0]
        
        filtered = filter(lambda x: x>10, routes)
        if len(set(filtered)) != 50:
            print(f"錯誤: 你必須服務所有客戶({len(set(filtered))}<50)。")
            print("-------------------------------------------------")

        self.nTruck1 = self.check(self.route1)
        self.nTruck2 = self.check(self.route2)
        self.nTruck3 = self.check(self.route3)
                
        self.gPlot()

    def check(self, route):
        depotLoad = 0
        nTruck = 0
        for i, n in enumerate(route):
            if i==0:
                load = 0
                continue
            if n >= 10:
                load += self.nodes[n].demand
            else:
                nTruck += 1
                print(f"倉庫 {route[0]} 車輛 {nTruck} 荷重 = {load}")
                if load > truckCapacity:
                    print(f"錯誤: 車輛荷重不可大於{truckCapacity}")
                load = 0
            depotLoad += self.nodes[n].demand
        if depotLoad > depotMaxSupply:
            print(f"{depotLoad}超過倉庫容量")
        else:
            print(f"倉庫 {route[0]} 供給量 {depotLoad}\n======================\n")
            
        return nTruck

    
    def __routeLength1(self)->float:
        cDepot = None
        rLength = 0
        for i in range(len(self.route1)-1):
            fromNodeId = self.route1[i]
            toNodeId = self.route1[i+1]
            rLength += self.arcs[fromNodeId][toNodeId].length
        return rLength
    
    def __routeLength2(self)->float:
        cDepot = None
        rLength = 0
        for i in range(len(self.route2)-1):
            fromNodeId = self.route2[i]
            toNodeId = self.route2[i+1]
            rLength += self.arcs[fromNodeId][toNodeId].length
        return rLength
    
    def __routeLength3(self)->float:
        cDepot = None
        rLength = 0
        for i in range(len(self.route3)-1):
            fromNodeId = self.route3[i]
            toNodeId = self.route3[i+1]
            rLength += self.arcs[fromNodeId][toNodeId].length
        return rLength
    
    def gPlot(self):
        """
        * To plot the routes 
        """
        # rid = ["Truck"]
#        idcount = 0
        linestyles = ['-','--','-.',':']
#        markers = ['o','^','s','.','v','*','p','1','<','>','x','X','d','D','+']
        plt.figure(figsize = (12,12))
        # plt.title(f"Objective={objective}\nTotal cost={totalCost}\nMax Route={maxRoute}\nMax Min Difference={difference}")
        length1 = self.__routeLength1()
        length2 = self.__routeLength2()
        length3 = self.__routeLength3()
        depot1routeCost = self.nTruck1*truckFixedCost
        depot2routeCost = self.nTruck2*truckFixedCost
        depot3routeCost = self.nTruck3*truckFixedCost
        depot1openCost = self.nodes[self.route1[0]].openCost
        depot2openCost = self.nodes[self.route2[0]].openCost
        depot3openCost = self.nodes[self.route3[0]].openCost
        totalCost = length1+length2+length3+depot1routeCost+depot2routeCost+depot3routeCost+depot1openCost+depot2openCost+depot3openCost
        
        plt.title(f"Depot{self.route1[0]}:{depot1openCost}, Length={length1}, Cost1={length1+depot1routeCost+self.nodes[self.route1[0]].openCost}\nDepot{self.route2[0]}:{depot2openCost}, Length={length2}, Cost2={length2+depot2routeCost+self.nodes[self.route2[0]].openCost}\nDepot{self.route3[0]}:{depot3openCost}, Length={length3}, Cost3={length3+depot3routeCost+self.nodes[self.route3[0]].openCost}\nTotal cost={totalCost}")
        plt.grid(True)
        plt.minorticks_on()
        
        candidates = self.nodes[:10]
        customers = self.nodes[10:]        
        
        for n in candidates:
            plt.plot(n.x, n.y, "go", ms=10)
            plt.text(n.x + 0.5, n.y + 0.5, f"{n.nid}({n.demand})")
        for n in customers:
            plt.plot(n.x, n.y, "bx")
            plt.text(n.x + 0.5, n.y + 0.5, f"{n.nid}({n.demand})")
        
        # for n in self.candidates:
        #     plt.plot(n.x, n.y, "bo")
        # tx = []
        # ty = []

        # for i in self.route:
        #     tx.append(self.nodes[i].x)
        #     ty.append(self.nodes[i].y)
        
        # plt.plot(tx,ty, "r-") ## plot truck route
        
        for i in range(len(self.route1)-1):
            fId = self.route1[i]
            tId = self.route1[i+1]
            if fId<10:
                linecolor = np.random.rand(3)
                ls = rd.choice(linestyles)
            plt.plot((self.nodes[fId].x, self.nodes[tId].x), (self.nodes[fId].y, self.nodes[tId].y), color="red", linestyle = ls)
            
        for i in range(len(self.route2)-1):
            fId = self.route2[i]
            tId = self.route2[i+1]
            if fId<10:
                linecolor = np.random.rand(3)
                ls = rd.choice(linestyles)
            plt.plot((self.nodes[fId].x, self.nodes[tId].x), (self.nodes[fId].y, self.nodes[tId].y), color="blue", linestyle = ls)
            
        for i in range(len(self.route3)-1):
            fId = self.route3[i]
            tId = self.route3[i+1]
            if fId<10:
                linecolor = np.random.rand(3)
                ls = rd.choice(linestyles)
            plt.plot((self.nodes[fId].x, self.nodes[tId].x), (self.nodes[fId].y, self.nodes[tId].y), color="m", linestyle = ls)
        
        # plt.legend(rid)



if __name__=="__main__":
    
    ## 每一個場站的最大容量為500單位，且有特定個開啟成本
    ## 每一車輛的最大容量為200單位
    ## 每點的需求量可由圖中點編號後面之括號看出，亦可參考產生的nodes.txt檔案
    ## 將路徑輸入在此，使用逗點(,)分隔。
    
    depot1_route = [0,20,52,19,49,21,58,0,
             14,34,31,0,
             43,42,25,0]
    
    depot2_route = [5,45,10,26,13,5,
             27,32,35,5,
             59,51,36,5]
    
    depot3_route = [9,56,57,46,9,
             24,47,48,9,
             30,12,23,9]

    g = Graph(depot1_route, depot2_route, depot3_route)
    
    






