void MemoryManager::showAllocatedMemory() {
    unsigned int beginAllocatedAddress, endAllocatedAddress, totalAllocated, processId, internalFragmentation, externalFragmentation;
    // INSERT YOUR CODE TO SHOW EACH ONE OF THE ALLOCATED MEMORY PARTITIONS, INCLUDING INTERNAL AND EXTERNAL (THE FOLLOWING) FRAGMENTATION
    // ...

    // for ... {  // for each partition...  
    // ...
    externalFragmentation = 0;
    for (int i=0; i<NUMBER_PARTITIONS; i++) {
        
        beginAllocatedAddress = _partitions[i]->getBeginAddress();
        endAllocatedAddress = _partitions[i]->getEndAddress();
        if (_partitions[i]->hasProcess()) {
            totalAllocated = getProcessSize(_partitions[i]->getProcess());
            processId = _partitions[i]->getProcess()->getId();
            internalFragmentation = _partitions[i]->getLength() - getProcessSize(_partitions[i]->getProcess());
        } else {
            totalAllocated = 0;
            processId = 0;
            internalFragmentation = _partitions[i]->getLength();
        }
        std::cout << "\tAllocd: " << "B=" << (beginAllocatedAddress) << ", \tE=" << (endAllocatedAddress) << ", \tT=" << (totalAllocated) << ", \tPID=" << (processId)
            << ", \tIF=" << (internalFragmentation) << ", \tEF=" << (externalFragmentation) << "\n";
    }
    // no not change the next line (the way information are shown)

    // }
}
void MemoryManager::allocateMemoryForProcess(Process* process) {
    unsigned int process_size = getProcessSize(process);

    // Alg. Best-fit descrito no livro [4]
    unsigned int initial_memory_waste = this->_size;
    unsigned int memory_waste = 0;
    Partition *best = nullptr;
    auto itBest = _freeList.end();

    for(auto it = _freeList.begin(); it != _freeList.end(); it++){
        if(process_size <= (*it)->getLength())
            memory_waste = (*it)->getLength() - process_size;
        else
            continue;

        if(initial_memory_waste > memory_waste){
            best = (*it);
            itBest = it;
            initial_memory_waste = memory_waste;
        }
    }

    if(best != nullptr){// Achou uma partição livre que caiba o processo
        unsigned int begin = best->getBeginAddress();
        unsigned int end = begin + process_size - 1;

        // Atualiza os limites de memória do processo
        process->setBeginMemory(begin);
        process->setEndMemory(end);

        // Atualiza a _freeList
        if(initial_memory_waste != 0){
            // Caso o processo não caiba perfeitamente na partição,
            //  então deve-se atualizar o endereço inicial da partição
            best->setBeginAddress(end+1);
        }else{
            // Caso o processo caiba perfeitamente na partição,
            //  então deleta ela da _freeList
            delete best;
            _freeList.erase(itBest);
        }

        // Adiciona a nova partição na _busyList
        addPartitionInOrder(new Partition(process, begin, end), true);

        //Simulator::getInstance()->getModel()->getProcessManager()->scheduler_choose(process);
    }else{// Processo entrou na fila de espera
        _queue.push_back(process);
    }
}
void MemoryManager::allocateMemoryForProcess(Process* process) {
    // INSERT YOUR CODE TO ALLOCATE MEMOTY FOR THE PROCESS
    // ...
    unsigned int processSize = getProcessSize(process);
    std::cout << "Process size :" << processSize << "\n" << "  Process id : " << process->getId() << "\n";
    for (int i=0; i<NUMBER_PARTITIONS; i++) {
        std::cout << "Size Partition :" << _partitions[i]->getLength() << "\n";
        if (_partitions[i]->getLength() > processSize) {
            if(!(_partitions[i]->hasProcess())) {
                _partitions[i]->setProcess(process);
                std::cout << "I was allocated\n";
                return;
            } else {
                _partitions[i]->addQueue(process);
                std::cout << "Entrei para fila" << process->getId() << "\n";
                return;
            }
        }
    }
    throw "não foi possível alocar";
}
bool functions::fits(Process *process, Partition *partition) {
    unsigned int processSize = getProcessSize(process);
    unsigned int partitionSize = partition->getLength();
    return (processSize <= partitionSize);
}
void MemoryManager::deallocateMemoryOfProcess(Process* process) {

    // Pequena proteção...
    if(process->getBeginMemory() == 0 &&
        process->getEndMemory() == 0)
        return;

    // Verifica se tem partições adjacentes
    auto itUp = _freeList.end();// Iterador da partição acima
    auto itDown = _freeList.end();// Iterador da partição abaixo
    for(auto it = _freeList.begin(); it != _freeList.end(); it++){
        auto tmp = (*it);
        if(tmp->getEndAddress()+1 == process->getBeginMemory()){
            itUp = it;
        }else if(tmp->getBeginAddress()-1 == process->getEndMemory()){
            itDown = it;
        }

        if(tmp->getBeginAddress() > process->getEndMemory())
            break;
    }

    auto pUp = (*itUp);// Partição acima
    auto pDown = (*itDown);// Partição abaixo

    // Espaço que ficará livre após este desalocamento
    unsigned int freeSize = 0;

    if(itUp != _freeList.end() && itDown != _freeList.end()){// Acima e abaixo
        // Existem partições livres acima e abaixo da partição que será desalocado

        pUp->setEndAddress(pDown->getEndAddress());// Atualiza o endereço final da partição acima
                                                   //  com o endereço final da partição abaixo [junta elas]
        freeSize = pUp->getLength();
        delete pDown;
        _freeList.erase(itDown);// Remove a partição abaixo
    }else if(itUp != _freeList.end() && itDown == _freeList.end()){// Só acima
        // Existe uma partição livre acima da que será desalocada

        pUp->setEndAddress(process->getEndMemory());// Atualiza o endereço final da partição acima
                                                    //  com o endereço final da partição que será desalocada
        freeSize = pUp->getLength();
    }else if(itUp == _freeList.end() && itDown != _freeList.end()){// Só abaixo
        // Existe uma partição livre abaixo da que será desalocada

        pDown->setBeginAddress(process->getBeginMemory());// Atualiza o endereço inicial da partição abaixo
                                                          //  com o endereço inicial da partição que será
                                                          //  desalocada
        freeSize = pDown->getLength();
    }else{// Isolada
        // A partição esta isolada, então simplesmente adicione
        //  ela na _freeList.
        auto tmp = new Partition(process->getBeginMemory(),
                                    process->getEndMemory());
        addPartitionInOrder(tmp, false);
        freeSize = tmp->getLength();
    }

    // Remove a partição do processo da _busyList
    for(auto it = _busyList.begin(); it != _busyList.end(); it++){
        auto tmp = (*it);
        if(tmp->getProcess() == process){
            delete (*it);
            _busyList.erase(it);
            break;
        }
    }

    // Zera a memória do processo
    process->setBeginMemory(0);
    process->setEndMemory(0);

    // Verifica se tem algum processo na fila que
    //  caiba no espaço gerado por esta desalocação
    unsigned int pSize = 0;
    for(auto it = _queue.begin(); it != _queue.end(); it++){
        auto tmp = (*it);
        pSize = getProcessSize(tmp);
        if(freeSize >= pSize){
            allocateMemoryForProcess(tmp);
            _queue.erase(it);
            break;
        }
    }
}