/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2014 Ruslan Baratov

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/

#include "cmFileLockPool.h"

#include <assert.h>

#include "cmFileLock.h"
#include "cmFileLockResult.h"
#include "cmAlgorithms.h"

cmFileLockPool::cmFileLockPool()
{
}

cmFileLockPool::~cmFileLockPool()
{
  cmDeleteAll(this->FunctionScopes);
  cmDeleteAll(this->FileScopes);
}

void cmFileLockPool::PushFunctionScope()
{
  this->FunctionScopes.push_back(new ScopePool());
}

void cmFileLockPool::PopFunctionScope()
{
  assert(!this->FunctionScopes.empty());
  delete this->FunctionScopes.back();
  this->FunctionScopes.pop_back();
}

void cmFileLockPool::PushFileScope()
{
  this->FileScopes.push_back(new ScopePool());
}

void cmFileLockPool::PopFileScope()
{
  assert(!this->FileScopes.empty());
  delete this->FileScopes.back();
  this->FileScopes.pop_back();
}

cmFileLockResult cmFileLockPool::LockFunctionScope(
    const std::string& filename, unsigned long timeoutSec)
{
  if (this->IsAlreadyLocked(filename))
    {
    return cmFileLockResult::MakeAlreadyLocked();
    }
  if (this->FunctionScopes.empty())
    {
    return cmFileLockResult::MakeNoFunction();
    }
  return this->FunctionScopes.back()->Lock(filename, timeoutSec);
}

cmFileLockResult cmFileLockPool::LockFileScope(
    const std::string& filename, unsigned long timeoutSec)
{
  if (this->IsAlreadyLocked(filename))
    {
    return cmFileLockResult::MakeAlreadyLocked();
    }
  assert(!this->FileScopes.empty());
  return this->FileScopes.back()->Lock(filename, timeoutSec);
}

cmFileLockResult cmFileLockPool::LockProcessScope(
    const std::string& filename, unsigned long timeoutSec)
{
  if (this->IsAlreadyLocked(filename))
    {
    return cmFileLockResult::MakeAlreadyLocked();
    }
  return this->ProcessScope.Lock(filename, timeoutSec);
}

cmFileLockResult cmFileLockPool::Release(const std::string& filename)
{
  for (It i = this->FunctionScopes.begin();
      i != this->FunctionScopes.end(); ++i)
    {
    const cmFileLockResult result = (*i)->Release(filename);
    if (!result.IsOk())
      {
      return result;
      }
    }

  for (It i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
    {
    const cmFileLockResult result = (*i)->Release(filename);
    if (!result.IsOk())
      {
      return result;
      }
    }

  return this->ProcessScope.Release(filename);
}

bool cmFileLockPool::IsAlreadyLocked(const std::string& filename) const
{
  for (CIt i = this->FunctionScopes.begin();
      i != this->FunctionScopes.end(); ++i)
    {
    const bool result = (*i)->IsAlreadyLocked(filename);
    if (result)
      {
      return true;
      }
    }

  for (CIt i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
    {
    const bool result = (*i)->IsAlreadyLocked(filename);
    if (result)
      {
      return true;
      }
    }

  return this->ProcessScope.IsAlreadyLocked(filename);
}

cmFileLockPool::ScopePool::ScopePool()
{
}

cmFileLockPool::ScopePool::~ScopePool()
{
  cmDeleteAll(this->Locks);
}

cmFileLockResult cmFileLockPool::ScopePool::Lock(
    const std::string& filename, unsigned long timeoutSec)
{
  cmFileLock *lock = new cmFileLock();
  const cmFileLockResult result = lock->Lock(filename, timeoutSec);
  if (result.IsOk())
    {
    this->Locks.push_back(lock);
    return cmFileLockResult::MakeOk();
    }
  else
    {
    delete lock;
    return result;
    }
}

cmFileLockResult cmFileLockPool::ScopePool::Release(
    const std::string& filename)
{
  for (It i = this->Locks.begin(); i != this->Locks.end(); ++i)
    {
    if ((*i)->IsLocked(filename))
      {
      return (*i)->Release();
      }
    }
  return cmFileLockResult::MakeOk();
}

bool cmFileLockPool::ScopePool::IsAlreadyLocked(
    const std::string& filename) const
{
  for (CIt i = this->Locks.begin(); i != this->Locks.end(); ++i)
    {
    if ((*i)->IsLocked(filename))
      {
      return true;
      }
    }
  return false;
}
