require 'sequel'

module BuildTool

    #
    # Provide method to add and retrieve entries from the history
    #
    module History

        module StateHelper

            # Needed for both Module and Command History
            STARTED = 0
            CANCELED_BY_USER = 1
            FINISHED_SUCCESSFUL = 2
            FINISHED_WITH_ERRORS = 3

            def state_str
                case self.state
                when STARTED
                    "STARTED"
                when CANCELED_BY_USER
                    "CANCELED"
                when FINISHED_SUCCESSFUL
                    "SUCCESS"
                when FINISHED_WITH_ERRORS
                    "ERRORS"
                else
                    "#{self.state.to_s}?"
                end
            end

            module ClassMethods
            end

            def self.included( klass )
                klass.extend ClassMethods
            end

        end

        # Represents a log entry for a command.
        class CommandLog < Sequel::Model( :command_logs )

            include StateHelper

            # A command can have many module events
            one_to_many :module_logs, :order => :id

            def duration
                if self.finished_at
                    dur = self.finished_at - self.started_at
                    "%02d:%02d" % [ dur.to_i / 60, (dur% 60 ).to_i ]
                else
                    "-----"
                end
            end

            # Call this if the command is finished. [:finished] will be set to Time.now,
            # [:state] to the given value and the object is saved.
            def finished( state )
                self.finished_at = Time.now
                self.state = state
                save
            end

            # Call this if the command is started. [:started] will be set to Time.now, [:state]
            # to STARTED and the object is saved.
            def started
                self.started_at = Time.now
                save
            end

            # Sequel Hook
            #
            # Make sure a finished command has one of the accepted finished states.
            def before_update
                super
                if !self.finished_at.nil?
                    raise StandardError, "Wrong state for finished Command" if ! [ FINISHED_SUCCESSFUL, FINISHED_WITH_ERRORS, CANCELED_BY_USER ].include? self.state
                end
            end

            def before_destroy
                self.module_logs_dataset.delete
                super
            end

            # Class Methods
            class << self

                # Loads the most recents entry from the database
                def most_recent( offset = nil )
                    if offset.nil?
                        return where( "id = (SELECT MAX(id) FROM #{table_name})" ).first
                    else
                        return order( :id.desc ).limit( 1, offset ).first
                    end
                end # def most_recent

                # Get the last :count issued commands
                #
                # @param [Integer] count Number of commands to return
                # @return [Array]  List containing the last count issued commands
                def last( count = 50 )
                    order(:id.desc).limit(count).all
                end

                # Get the last :count command entries containing module :modname.
                def last_by_module( modname, count = 3 )
                    where( 'id in ( select distinct command_log_id from module_logs where module = ? )', modname ).order( :id.desc ).first( count );
                end

                # Get all commands older than :days
                def older_than
                    where( 'finished_at < ?', Date.today - 10 )
                end


            end # ClassMethods

        end

        # Represents a module event.
        #
        # A module event is updating, compiling etc. a module.
        class ModuleLog < Sequel::Model( :module_logs )

            include StateHelper

            # A module event belongs to a command
            many_to_one :command_log

            def duration
                if self.finished_at
                    dur = self.finished_at - self.started_at
                    "%02d:%02d" % [ dur.to_i / 60, (dur% 60 ).to_i ]
                else
                    "-----"
                end
            end

            # Call this if the command is finished. [:finished] will be set to Time.now,
            # [:state] to the given value and the object is saved.
            def finished( state )
                self.finished_at = Time.now
                self.state = state
                save
            end

            # Call this when the command is started. [:started] will be set to Time.now and
            # the object is saved.
            def started
                self.started_at = Time.now
                save
            end

            # Sequel Hook
            #
            # Make sure a finished command has one of the accepted finished states.
            def before_update
                super
                if !self.finished_at.nil?
                    raise StandardError, "Wrong state for finished Command" if ! [ FINISHED_SUCCESSFUL, FINISHED_WITH_ERRORS, CANCELED_BY_USER ].include? self.state
                end
            end

            class << self

            end # class self

        end

    end # module BuildTool::History

end # module BuildTool