
import logging
import os
import signal
import re

from tornado import gen

from delivery.models.db_models import StagingStatus
from delivery.exceptions import RunfolderNotFoundException, InvalidStatusException,\
    ProjectNotFoundException, TooManyProjectsFound

from delivery.services.file_system_service import FileSystemService

log = logging.getLogger(__name__)


class StagingService(object):
    """
    Starting in this context means copying a directory or file to a separate directory before delivering it.
    This service handles that in a asynchronous way. Copying operations (right nwo powered by rsync) can be
    started, and their status monitored by querying the underlying database for their status.
    """

    # TODO On initiation of a Staging service, restart any ongoing stagings
    # since they should all have been killed.
    # And if we do so we need to make sure that the Staging service
    # acts as a singleton, look at:
    # http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html
    #
    # Alternative suggestion from Steinar on how to solve the problem, which is probably better:
    # "Do you mean to ensure that only one thread tries doing that at a time? An idea could
    #  be to take a database lock to ensure this, i.e. fetch all objects in the unfinished
    #  state, restart them, change the status and then commit, locking the sqlite database
    #  briefly while doing so (I think row level locking is limited in sqlite.)"
    #  / JD 20161111

    def __init__(self,
                 staging_dir,
                 external_program_service,
                 staging_repo,
                 runfolder_repo,
                 project_dir_repo,
                 project_links_directory,
                 session_factory,
                 file_system_service = FileSystemService):
        """
        Instantiate a new StagingService
        :param staging_dir: the directory to which files/dirs should be staged
        :param external_program_service: a instance of ExternalProgramService
        :param staging_repo: a instance of DatabaseBasedStagingRepository
        :param runfolder_repo: a instance of FileSystemBasedRunfolderRepository
        :param project_dir_repo: a instance of GeneralProjectRepository
        :param project_links_directory: a path to a directory where links will be created temporarily
                                        before they are rsynced into staging (for batched deliveries etc)
        :param session_factory: a factory method which can produce new sqlalchemy Session instances
        """
        self.staging_dir = staging_dir
        self.external_program_service = external_program_service
        self.staging_repo = staging_repo
        self.runfolder_repo = runfolder_repo
        self.project_dir_repo = project_dir_repo