#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "lcgrand.h"

#define Q_LIMIT_i 100
#define Q_LIMIT_r 100
#define BUSY 1
#define IDLE 0
#define arrival_i1    0
#define arrival_i2    1
#define arrival_r     2
#define departure_i1  3
#define departure_r1  4
#define departure_r2  5
#define termination   6 

typedef struct event{
        float time;
        int event_type;
        struct event *next;
        } *EVENT;

EVENT event;//事件 

int server_status_i1, server_status_r1,server_status_r2;//server 狀態 busy或idle 
int num_in_qi, num_in_qr; //inspection & repair 已使用queue數目 
int event_type; //系統當前資料型態 
int count; //計數器，計算多少人次成功從inspection離開 
float time_arrival_i[Q_LIMIT_i], time_arrival_r[Q_LIMIT_r];//inspection & repair的queue
float sim_time;//使用者想模擬多久 
float present_time;//系統當前時間 
float cumulative_using_i1, cumulative_using_r1, cumulative_using_r2;//累計使用inspection & repair 各個server的時間 
float last_BUSY_start_i1, last_BUSY_start_r1, last_BUSY_start_r2;// 最近一次 inspection & repair 各個server 是開始busy的時刻 
int Max_capacity_i;//inspection 最大服務數目; 


int initialize(float);//初始化(預計模擬的時間) 
int timing(EVENT); //更新現在時間,系統最新狀態(事件list) 
int arrive_i(float,int);//inspection到達事件發生時，系統inspection要進行的事，包含立即處理並產生離開事件,新到達事件or加入queue(事件發生時間,事件來源) 
int arrive_r(float);//repair到達事件發生時，系統inspection要進行的事，包含立即處理並產生離開事件or加入queue(事件發生時間,事件來源)
int depart_i(float);//inspection離開事件發生時，系統inspection要進行的事，包含從queue讀取新資料並機率產生離開事件or改成idle(事件發生時間) 
int depart_r(float,int);//repair離開事件發生時，系統repair要進行的事，包含從queue讀取新資料並機率產生離開事件,or改成idle(事件發生時間,事件來源)

int report(FILE *);//資料統計 
int generate_event(int,float);//產生事件並加入事件串列中(事件資料型態,事件發生時間) 
int add_in_q(float[],float,int*); //將資料加入queue中(目標queue,要傳進去的資料,已使用queue的數目) 
float read_from_q(float[],int*);//從queue中讀取一筆資料(目標queue,已使用queue的數目) 

float uniform(float,float);
float expon(float);





int main(){
    FILE *outfile;
    outfile = fopen("d:\\example file\\bus_maintenance_depot.dat", "a");
  
    printf("Input the desired simulation time:\n");
    scanf("%f",&sim_time);
  
    initialize(sim_time);
    timing(event);
    
    while(event_type != termination){
                     switch(event_type){
                                        case 0:
                                        case 1:
                                             arrive_i(present_time,event_type);
                                             break;
                                        case 2:
                                             arrive_r(present_time);
                                             break;
                                        case 3:
                                             depart_i(present_time);
                                             break; 
                                        case 4:
                                        case 5:
                                             depart_r(present_time,event_type);
                                             break;
                                             }
                     timing(event);                   
                     }
    report(outfile);                   
    fclose(outfile);
    system("PAUSE");	
    return 0;
    }

int initialize(float simulation_time){
    present_time = 0;
    
    event = NULL;
    server_status_i1 = IDLE; server_status_r1 = IDLE; server_status_r2 = IDLE;
    num_in_qi = 0; num_in_qr = 0;  
    event_type = 7;
    generate_event(arrival_i1, present_time + expon(2));
    generate_event(termination, simulation_time);
    count = 0;
    cumulative_using_i1 = 0; cumulative_using_r1 = 0; cumulative_using_r2 = 0;
    last_BUSY_start_i1 = 0; last_BUSY_start_r1 = 0; last_BUSY_start_r2 = 0;
    Max_capacity_i = 1;
    return 0;
    }



int timing(EVENT event_recieved){
    present_time = event_recieved->time;
    event_type = event_recieved->event_type; 
    EVENT temp = NULL;
    temp = event_recieved;
    event_recieved = event_recieved->next;
    free(temp);
    return 0;
    }


int arrive_i(float time_arrival,int event_type_a){
    if(server_status_i1 == IDLE){
                        server_status_i1 = BUSY;
                        last_BUSY_start_i1 = present_time;
                        generate_event(departure_i1, present_time + uniform(0.25,1.05));
                        if(event_type_a == arrival_i1)
                                      generate_event(arrival_i1, present_time + expon(2));
                        }                        
    else{
         if(num_in_qi < Q_LIMIT_i){
             add_in_q(time_arrival_i, time_arrival, &num_in_qi);
             if(event_type_a == arrival_i1)
                           generate_event(arrival_i1, present_time + expon(2));
             Max_capacity_i = (num_in_qi + 1 > Max_capacity_i)? (num_in_qi + 1) : Max_capacity_i; 
             }       
         else{
              printf("Queue of inspection shop is empty, so stop simulation clock");
              generate_event(termination, present_time);
              }        
        }
    return 0;
    }                              
                       
int arrive_r(float time_arrival){
    if(server_status_r1 == IDLE){
                        server_status_r1 = BUSY;
                        last_BUSY_start_r1 = present_time;
                        generate_event(departure_r1, present_time + uniform(2.1,4.5));
                        }
    else if(server_status_r2 == IDLE){
                        server_status_r2 = BUSY;
                        last_BUSY_start_r2 = present_time;
                        generate_event(departure_r2, present_time + uniform(2.1,4.5));
                        }
    else{
         if(num_in_qr < Q_LIMIT_r)
             add_in_q(time_arrival_r, time_arrival, &num_in_qr);
         else{
              printf("Queue of repair shop is empty, so stop simulation clock");
              generate_event(termination, present_time);
              }        
        }
    return 0;
    }                       
         
      
int depart_i(float time_departure){
    if(uniform(0,1) <= 0.3) 
                    generate_event(arrival_r, present_time);
    else
         count++;
    
    if(num_in_qi == 0){
                 cumulative_using_i1 = present_time - last_BUSY_start_i1; 
                 server_status_i1 = IDLE;
                 }    
    else{
         read_from_q(time_arrival_i, &num_in_qi); 
         generate_event(departure_i1, present_time + uniform(0.25,1.05));
         }
    return 0;
    }


int depart_r(float time_departure,int event_type_d){
    if(uniform(0,1) <= 0.5)
                    generate_event(arrival_i2, present_time);
    if(num_in_qr == 0){
                 switch(event_type_d){
                                    case 4:
                                         cumulative_using_r1 = present_time - last_BUSY_start_r1;
                                         server_status_r1 = IDLE; 
                                         break;
                                    case 5:
                                         cumulative_using_r2 = present_time - last_BUSY_start_r2;
                                         server_status_r2 = IDLE;
                                         break;
                                    }   
                 }   
    else{
         read_from_q(time_arrival_r, &num_in_qr); 
         switch(event_type_d){
                                    case 4:
                                         generate_event(departure_r1, present_time + uniform(2.1,4.5)); 
                                         break;
                                    case 5:
                                         generate_event(departure_r2, present_time + uniform(2.1,4.5)); 
                                         break;
                                    }   
         }
    return 0;
    }


int report(FILE *o_file){
    static float utilization_i1, utilization_r1, utilization_r2;
    
    fprintf(o_file, "average no. successful passing inspection per hr = %f\n", count/present_time);
    
    if(server_status_i1 == BUSY)
                        utilization_i1 = (cumulative_using_i1 + present_time - last_BUSY_start_i1)/present_time;
    else
                        utilization_i1 = cumulative_using_i1/present_time;                   
    if(server_status_r1 == BUSY)
                        utilization_r1 = (cumulative_using_r1 + present_time - last_BUSY_start_r1)/present_time;
    else
                        utilization_r1 = cumulative_using_r1/present_time;  
    if(server_status_r2 == BUSY)
                        utilization_r2 = (cumulative_using_r2 + present_time - last_BUSY_start_r2)/present_time;
    else
                        utilization_r2 = cumulative_using_r2/present_time;
    fprintf(o_file, "utilzation\n");                    
    fprintf(o_file, "inspecton_server_1  repair_server_1  repair_server_2\n");
    fprintf(o_file, "%15.4f %15.4f %15.4f",utilization_i1,utilization_r1,utilization_r2);
    
    fprintf(o_file, "maximum capcity of inspection depot with p = 0.5 is %d\n", Max_capacity_i);
    return 0;
    }  

  

int generate_event(int next_event_type,float next_event_time){   
    
    EVENT ptr = (EVENT)malloc(sizeof(struct event));
    ptr->event_type = next_event_type;
    ptr->time = next_event_time;
    ptr->next = NULL;
    
    if(event == NULL)
         event = ptr;
    else if(ptr->time <= event->time){
         ptr->next = event;
         event = ptr;
         }
    else{
         EVENT current = event; 
         EVENT prev = NULL;
                  
         while(current != NULL && ptr->time > current->time){
                         prev = current;
                         current = current->next;
                         }
         prev->next = ptr;
         ptr->next = current;
         }
    return 0;
    }

int add_in_q(float data[],float data_arrival,int *num_in_qu){
    data[*num_in_qu] = data_arrival;
    (*num_in_qu)++;
    return 0;
    }


float read_from_q(float data[],int *num_in_qu){
    (*num_in_qu)--; 
    static float temp;
    temp = data[0];
    int i;
    for(i=0; i< *num_in_qu - 1; i++)
            data[i] = data[i+1];
    return temp;
    }


float uniform(float lower,float upper){
      return lower + lcgrand(1)*(upper - lower);
      }

float expon(float mean){
      return -mean*log(lcgrand(1));
      }
