// ===========================================================
// File: Server.cpp
//
// Implementation of the Server class. Server handles all
// request from Com and Client. It is responsible to
// simultanously service them both while handling and
// maintaining multiple list/queues. By servicing both a
// console and user program, it simulates the activities of
// a simple multi-process virtual OS.
// ===========================================================

#include <iostream.h>
#include <fstream.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "Pcib.h"
#include "List.h"
#include "Semaphore.h"
#include "Message.h"
#include "Server.h"

// ===========================================================
// Identifier:  Server()
//
// Comments:    Server default constructor. Creates two
//              structure, an active list and semaphore(s)
// ===========================================================

Server::Server()
{
    this->ActiveList = new List;

    for (int i = 0; i < MAX_SEM; i++)
    {
        this->Sem[i] = new Semaphore;
        this->Sem[i]->semId = i;
    }
}

// ===========================================================
// Identifier:  ~Server()
//
// Comments:    Server destructor. Prevents memory leak
// ===========================================================

Server::~Server()
{
    delete this->ActiveList;
    this->ActiveList = NULL;

    for (int i = 0; i < MAX_SEM; i++)
    {
        delete this->Sem[i];
        this->Sem[i] = NULL;
    }
}

// ===========================================================
// Identifier:  LsCommand(const ComMsg)
//
// Parameter:   m1      -- Communication message
//
// Comments:    List the informations in the active queue.
//              Also can list information of all semaphores
//              (authorize queue & waiting queue) depending
//              if a semaphore option is present.
// ===========================================================

void Server::LsCommand(const ComMsg m1)
{
    cout << "Active List:\n";
    this->DisplayList(this->ActiveList->head);

    if (m1.option == 's')
    {
        for (int i = 0; i < MAX_SEM; i++)
        {
            cout << "Sem ID: "    << i;

            if (!this->Sem[i]->AuthorizedList->IsEmpty() ||
                !this->Sem[i]->WaitingList->IsEmpty())
            {
                cout << "\tOccupied";
                cout << "\nSem Key: "   << this->Sem[i]->semKey;
                cout << "\nSem Value: " << this->Sem[i]->semValue;
                cout << "\n\n";
                cout << "Authorized List:\n";
                this->DisplayList(this->Sem[i]->AuthorizedList->head);
                cout << "Waiting List:\n";
                this->DisplayList(this->Sem[i]->WaitingList->head);
            }
            else
                cout << "\tFree\n";
        }
    }
}

// ===========================================================
// Identifier:  ExitCommand()
//
// Comments:    Calls Server destructor and cleans up all
//              list/queues to prevent memory leak
// ===========================================================

void Server::ExitCommand()
{
    this->~Server();
}

// ===========================================================
// Identifier:  bool GetSemId(const int)
//
// Parameter:   semKey      -- semaphore key
//
// Comments:    Gets the semaphore Id by providing a key
//
// Returns:     semId       -- the semaphore Id
// ===========================================================

int Server::GetSemId(const int semKey)
{
    int semId = -1;

    for (int i = 0; i < MAX_SEM; i++)
    {
        if (semKey == this->Sem[i]->semKey)
        {
            semId = this->Sem[i]->semId;
            break;
        }
    }
    return semId;
}

// ===========================================================
// Identifier:  bool Verify(const int, const int)
//
// Parameter:   semId       -- semaphore ID
//              pid         -- process ID
//
// Comments:    Verifies the semdId and PID.
//              This is usually called by semaphore actions.
//
// Returns:     true if valid, false otherwise
// ===========================================================

bool Server::Verify(const int semId, const int pid)
{
    if (semId < 0 || semId > MAX_SEM - 1)
        return false;
    else
        return this->Sem[semId]->AuthorizedList->ContainPid(pid);
}

// ===========================================================
// Identifier:  bool NewKey(const int)
//
// Parameter:   semKey      -- semaphore key
//
// Comments:    Test if semaphore key already exist
//
// Returns:     true if exist, false otherwise
// ===========================================================

bool Server::NewKey(const int semKey)
{
    bool newKey = false;

    for (int i = 0; i < MAX_SEM; i++)
    {
        if (semKey != this->Sem[i]->semKey)
            newKey = true;
        else
        {
            newKey = false;
            break;
        }
    }
    return newKey;
}

// ===========================================================
// Identifier:  bool FreeSem(int&)
//
// Parameter:   &newSem     -- new Semaphore ID
//
// Comments:    Test if there is a free semaphore. If there
//              is, then give newSem a new semId. The new
//              semaphore is returned by reference.
//
// Returns:     true if free semaphore exist, false otherwise
//              newSem is set to new Semaphore, otherwise -1
// ===========================================================

bool Server::FreeSem(int &newSem)
{
    bool freeSem = false;
    newSem = -1;

    for (int i = 0; i < MAX_SEM; i++)
    {
        if (this->Sem[i]->AuthorizedList->IsEmpty())
        {
            newSem = i;
            freeSem = true;
            break;
        }
    }
    return freeSem;
}

// ===========================================================
// Identifier:  Insert(const int, const int, const ComMsg)
//
// Parameter:   pid         -- process ID
//              pipeId      -- pipe ID
//              m1          -- communication message
//
// Comments:    Inserts a new PCIB into the active list.
//              If there is the option of creating a log file,
//              method will do so with the file name equal
//              to the process ID. All initial informations
//              are recording into the file. Later, the file
//              will append relevant informations about
//              system call tracing.
// ===========================================================

void Server::Insert(const int pid, const int pipeId, const ComMsg m1)
{
    PCIB pcib;

    pcib.PID = pid;
    pcib.timeStamp = time(NULL);
    memcpy(pcib.execFile, m1.execName, EXE_LENGTH);
    pcib.pipeId = pipeId;

    if (m1.option == LOG_OPTION)
    {
        char buff[256];
        memset(buff, '\0', sizeof(buff));
        char *time = ctime(&pcib.timeStamp);
        sprintf(pcib.logFileName, "%d", pid);
        fstream out(pcib.logFileName, ios::app);
        sprintf(buff, "Timestamp: %s", time);
        sprintf(buff, "%sExecutable: %s\n", buff, pcib.execFile);
        sprintf(buff, "%sProcess PID: %d\n", buff, pcib.PID);
        sprintf(buff, "%sPipe ID: %d\n\n", buff, pcib.pipeId);
        sprintf(buff, "%sSemaphore Request Log...\n\n", buff);
        out.write(buff, sizeof(buff));
        out.close();
    }
    this->ActiveList->Insert(pcib);
}

// ===========================================================
// Identifier:  DisplayList(pNode)
//
// Parameter:   pNode       -- also pPCIB, a pointer to PCIB
//
// Comments:    Displays all PCIB information in the current
//              list. If list is empty, nothing will be
//              displayed.
// ===========================================================

void Server::DisplayList(pNode curr)
{
    if (curr != NULL)
    {
        cout << "PID\tEXEC FILE\tTIME STAMP\tLOG FILE\n";
    
        while (curr != NULL)
        {
            cout << curr->PID           << "\t";
            cout << curr->execFile      << "\t\t";
            cout << curr->timeStamp     << "\t";

            if (curr->logFileName[0] != '\0')
                cout << curr->logFileName   << "\n";
            else
                cout << "N/A\n";

            curr = curr->next;
        }
    }
    else
        cout << "Empty.\n";
    cout << endl;
}

// ===========================================================
// Identifier:  ReplyMsg SemAction(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Determines the action (purpose) of the
//              semaphore and calls the appropriate method
//              to service it.
//
// Returns:     m3      -- reply message
// ===========================================================

ReplyMsg Server::SemAction(const RequestMsg m2)
{
    ReplyMsg m3;

    switch (m2.purpose)
    {
        case GET_SEM:       {m3 = this->getSem(m2);     }   break;
        case SIGNAL_SEM:    {m3 = this->sinalSem(m2);   }   break;
        case WAIT_SEM:      {m3 = this->waitSem(m2);    }   break;
        case RELEASE_SEM:   {m3 = this->releaseSem(m2); }   break;
        case VS_EXIT:       {     this->vsexit(m2);     }   break;
    }
    return m3;
}

// ===========================================================
// Identifier:  ReplyMsg getSem(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Gets a new semaphore. If it is a new key
//              (or maybe already an existing one) and there
//              is a free semaphore. Method will issue a
//              semaphore by inserting the pcib into the
//              authorized list.
//
// Returns:     m3      -- reply message, 0 success, -1 fail
// ===========================================================

ReplyMsg Server::getSem(const RequestMsg m2)
{
    int newSem = -1;
    ReplyMsg m3;
    m3.returnValue = -1;

    PCIB pcib;
    pcib.PID = m2.PID;
    pcib.timeStamp = m2.timeStamp;

    this->ActiveList->InsertTimeStamp(m2.PID, m2.timeStamp);

    if (this->NewKey(m2.semKey) && this->FreeSem(newSem))
    {
        this->Sem[newSem]->semKey = m2.semKey;
        this->Sem[newSem]->semValue = m2.semInitValue;
        this->Sem[newSem]->AuthorizedList->Insert(pcib);
        m3.returnValue = 0;
    }
    else if (!this->NewKey(m2.semKey))
    {
        newSem = this->GetSemId(m2.semKey);
        this->Sem[newSem]->AuthorizedList->Insert(pcib);
        m3.returnValue = 0;
    }
    m3.semId = newSem;
    return m3;
}

// ===========================================================
// Identifier:  ReplyMsg sinalSem(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Verifies the semId and PID. If such
//              semaphore exist, increment the semValue.
//              This process may wake-up a process in
//              the waiting queue.
//
// Returns:     m3      -- reply message, 0 success, -1 fail
// ===========================================================

ReplyMsg Server::sinalSem(const RequestMsg m2)
{
    ReplyMsg m3;
    m3.semId = m2.semId;
    m3.returnValue = -1;

    this->ActiveList->InsertTimeStamp(m2.PID, m2.timeStamp);
    
    if (this->Verify(m2.semId, m2.PID))
    {
        m3.returnValue = 0;
        this->Sem[m2.semId]->semValue++;

        if (this->Sem[m2.semId]->semValue <= 0)
        {
            PCIB pcib = this->Sem[m2.semId]->WaitingList->Dequeue();
            this->ActiveList->Insert(pcib);

            if (pcib.pipeId != -1)
                write(pcib.pipeId, &m3, sizeof(m3));
        }
    }
    return m3;
}

// ===========================================================
// Identifier:  ReplyMsg waitSem(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Verifies the semId and PID. If such
//              semaphore exist, decrement the semValue.
//              This process may prempt a process in
//              the active queue.
//
// Returns:     m3      -- reply message, 0 success, -1 fail
// ===========================================================

ReplyMsg Server::waitSem(const RequestMsg m2)
{
    ReplyMsg m3;
    m3.semId = m2.semId;
    m3.returnValue = -1;

    this->ActiveList->InsertTimeStamp(m2.PID, m2.timeStamp);
    
    if (this->Verify(m2.semId, m2.PID))
    {
        this->Sem[m2.semId]->semValue--;

        if (this->Sem[m2.semId]->semValue < 0)
        {
            PCIB pcib = this->ActiveList->Delete(m2.PID);
            this->Sem[m2.semId]->WaitingList->Insert(pcib);
        }
        else
            m3.returnValue = 0;
    }
    return m3;
}

// ===========================================================
// Identifier:  ReplyMsg releaseSem(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Verifies the semId and PID. If such
//              semaphore exist, delete the process from
//              the authorized list and release (cancel)
//              a semaphore.
//
// Returns:     m3      -- reply message, 0 success, -1 fail
// ===========================================================

ReplyMsg Server::releaseSem(const RequestMsg m2)
{
    ReplyMsg m3;
    m3.semId = m2.semId;
    m3.returnValue = -1;

    this->ActiveList->InsertTimeStamp(m2.PID, m2.timeStamp);
    
    if (this->Verify(m2.semId, m2.PID))
    {
        this->Sem[m2.semId]->AuthorizedList->Delete(m2.PID);
        m3.returnValue = 0;
    }
    return m3;
}

// ===========================================================
// Identifier:  vsexit(const RequestMsg)
//
// Parameter:   m2      -- request message
//
// Comments:    Deletes the process from existence in all
//              list and semaphore. Resource is returned to
//              Server.
// ===========================================================

void Server::vsexit(const RequestMsg m2)
{
    PCIB pcib = this->ActiveList->Delete(m2.PID);
    close(pcib.pipeId);

    for (int i = 0; i < MAX_SEM; i++)
    {
        pcib = this->Sem[i]->AuthorizedList->Delete(m2.PID);
        close(pcib.pipeId);
        pcib = this->Sem[i]->WaitingList->Delete(m2.PID);
        close(pcib.pipeId);
    }
}