#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = TimeSheets_spec.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
#               by Chris Schlaeger <chris@linux.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#

require 'rubygems'
require 'support/DaemonControl'
require 'taskjuggler/apps/Tj3TsSender'
require 'taskjuggler/apps/Tj3TsReceiver'
require 'taskjuggler/apps/Tj3TsSummary'

class TaskJuggler

  describe TimeSheets do

    include DaemonControl

    before(:all) do
      # Make sure we run in the same directory as the spec file.
      @pwd = pwd
      cd(File.dirname(__FILE__))
      ENV['TASKJUGGLER_DATA_PATH'] = "../"

      cleanup
      startDaemon(<<'EOT'
  emailDeliveryMethod: smtp
  smtpServer: foobar.com
_timesheets:
  projectId: tstest
  senderEmail: foo@example.com
  _sender:
    hideResource: '~isleaf()'
  _summary:
    sheetRecipients:
      - archive@example.com
    digestRecipients:
      - archive@example.com
      - crew@example.com
EOT
                 )
      prj = <<'EOT'
project tstest "Time Sheet Test" 2011-03-14 +2m {
  trackingscenario plan
  now ${projectstart}
}

flags important, late

resource "Team" {
  resource r1 "R1" {
    email "r1@example.com"
  }
  resource r2 "R2" {
    email "r2@example.com"
  }
}
task t1 "T1" {
  effort 2.5d
  allocate r1
}
task t2 "T2" {
  depends !t1
  effort 2.5d
  allocate r1
}
task t3 "T3" {
  effort 10d
  allocate r2
}
EOT
      res = stdIoWrapper(prj) do
        Tj3Client.new.main(%w( --unsafe add . ))
      end
      unless res.stdErr.include?("Info: Project(s) . added")
        raise "Project not loaded: #{res.stdErr}"
      end
      raise "Can't load project" unless res.returnValue == 0

      res = stdIoWrapper do
        Tj3TsSender.new.main(%w( --dryrun --silent -e 2011-03-21 ))
      end
      if res.stdErr != ''
        raise "Tj3TsSender failed: #{res.stdErr}"
      end
      @tss_mails = collectMails(res.stdOut)
      raise "Timesheet generation failed" unless res.returnValue == 0

      @sheet1 = <<'EOT'
# --------8<--------8<--------
timesheet r1 2011-03-14-00:00-+0000 - 2011-03-21-00:00-+0000 {
  task t1 {
    work 30.0%
    remaining 2.0d
    status red "More work" {
      flags important, late
      details -8<-
      This is more work than expected.
      ->8-
    }
  }
  task t2 {
    work 50.0%
    remaining 0.0d
    status green "All work done!"
  }

  newtask t4 "A new job" {
    work 20%
    remaining 1.0d
    status green "May be a good idea" {
      summary -8<-
      I thought this might be useful work.
      ->8-
    }
  }
}
# -------->8-------->8--------
EOT
      @sheet2 = <<'EOT'
# --------8<--------8<--------
timesheet r2 2011-03-14-00:00-+0000 - 2011-03-21-00:00-+0000 {
  # Task: T3
  task t3 {
    work 100.0%
    remaining 5.0d
    status green "What a job!"
  }

  status yellow "I'm not feeling good!" {
    summary -8<-
    We all live on a yellow submarine!
    ->8-
  }
}
# -------->8-------->8--------
EOT
      @tsr_mails = []
      [ @sheet1, @sheet2 ].each do |sheet|
        mail = Mail.new do
          subject "Timesheet"
          content_type [ 'text', 'plain', { 'charset' => 'UTF-8' } ]
          content_transfer_encoding 'base64'
          body sheet.to_base64
        end
        mail.to = 'taskjuggler@example.com'
        mail.from 'r@example.com'
        res = stdIoWrapper(mail.to_s) do
          Tj3TsReceiver.new.main(%w( --dryrun --silent ))
        end
        @tsr_mails += collectMails(res.stdOut)
        unless res.returnValue == 0
          raise "Timesheet processing failed: #{res.stdErr}"
        end
      end

      res = stdIoWrapper(prj) do
        Tj3Client.new.main(%w( --unsafe --silent add . TimeSheets/2011-03-21/all.tji ))
      end
      unless res.returnValue == 0
        raise "Project reloading failed: #{res.stdErr}"
      end

      res = stdIoWrapper do
        Tj3TsSummary.new.main(%w( --dryrun --silent -e 2011-03-21 ))
      end
      @sum_mails = collectMails(res.stdOut)
      unless res.returnValue == 0
        raise "Summary generation failed: #{res.stdErr}"
      end
    end

    after(:all) do
      stopDaemon
      cleanup
      cd(@pwd)
    end

    describe TimeSheetSender do

      it 'should have generated 2 mails' do
        @tss_mails.length.should == 2
      end

      it 'should have email sender foo@example.com' do
        @tss_mails.each do |mail|
          mail.from[0].should == 'foo@example.com'
        end
      end

      it 'should have proper email receivers' do
        @tss_mails[0].to[0].should == 'r1@example.com'
        @tss_mails[1].to[0].should == 'r2@example.com'
      end

      it 'should generate properly dated headers' do
        countLines(@tss_mails[0].parts[0].decoded,
                   'timesheet r1 2011-03-14-00:00-+0000 - ' +
                   '2011-03-21-00:00-+0000').should == 1
        countLines(@tss_mails[1].parts[0].decoded,
                   'timesheet r2 2011-03-14-00:00-+0000 - ' +
                   '2011-03-21-00:00-+0000').should == 1
      end

      it 'should have matching timesheets in body and attachment' do
        @tss_mails.each do |mail|
          bodySheet = extractTimeSheet(mail.parts[0].decoded)
          attachedSheet = extractTimeSheet(mail.part[1].decoded)
          bodySheet.should == attachedSheet
        end
      end

    end

    describe TimeSheetReceiver do

      it 'should have generated 2 mails' do
        @tsr_mails.length.should == 2
      end

      it 'should have email sender foo@example.com' do
        @tsr_mails.each do |mail|
          mail.from[0].should == 'foo@example.com'
        end
      end

      it 'should have proper email receivers' do
        @tsr_mails[0].to[0].should == 'r1@example.com'
        @tsr_mails[1].to[0].should == 'r2@example.com'
      end

      it 'should have stored timesheets' do
        @sheet1.should == File.read('TimeSheets/2011-03-21/r1_2011-03-21.tji')
        @sheet2.should == File.read('TimeSheets/2011-03-21/r2_2011-03-21.tji')
      end

      it 'should report an error on bad keyword' do
        sheet = <<'EOT'
# --------8<--------8<--------
timesheet r2 2011-03-14-00:00-+0000 - 2011-03-21-00:00-+0000 {

  task t3 {
    wirk 100.0%
    remaining 5.0d
    status green "All green!"
  }
}
# -------->8-------->8--------
EOT
        mail = Mail.new do
          subject "Timesheet"
          content_type [ 'text', 'plain', { 'charset' => 'UTF-8' } ]
          content_transfer_encoding 'base64'
          body sheet.unix2dos.to_base64
        end
        mail.to = 'taskjuggler@example.com'
        mail.from 'r@example.com'
        res = stdIoWrapper(mail.to_s) do
          Tj3TsReceiver.new.main(%w( --dryrun --silent ))
        end
        countLines(res.stdErr,
                   /\.\:5\: Error\: Unexpected token 'wirk' found\./).should == 1
        res.returnValue.should == 1
      end

    end

    describe TimeSheetSummary do

      it 'should have generated 4 mails' do
        @sum_mails.length.should == 4
      end

      it 'should have proper email receivers' do
        @sum_mails[0].to[0].should == 'archive@example.com'
        @sum_mails[1].to[0].should == 'archive@example.com'
        @sum_mails[2].to[0].should == 'archive@example.com'
        @sum_mails[3].to[0].should == 'crew@example.com'
      end

      it 'should have proper email senders' do
        @sum_mails[0].from[0].should == 'r1@example.com'
        @sum_mails[1].from[0].should == 'r2@example.com'
        @sum_mails[2].from[0].should == 'foo@example.com'
        @sum_mails[3].from[0].should == 'foo@example.com'
      end

    end

    private

    def countLines(text, pattern)
      c = 0
      if pattern.is_a?(Regexp)
        text.each_line do |line|
          c += 1 if line =~ pattern
        end
      else
        text.each_line do |line|
          c += 1 if line.include?(pattern)
        end
      end
      c
    end

    def extractTimeSheet(lines)
      sheet = nil
      lines.each_line do |line|
        if line =~ /^# --------8<--------8<--------/
          sheet = ""
        elsif line =~ /^# -------->8-------->8--------/
          raise 'Found end marker, but no start marker' unless sheet
          return sheet
        elsif sheet
          sheet += line
        end
      end
      raise "No end marker found"
    end

    def collectMails(lines)
      mails = []
      mailLines = nil
      lines.each_line do |line|
        if line =~ /^-- Email Start ---/
          mailLines = ""
        elsif line =~ /^-- Email End ---/
          raise 'Found end marker, but no start marker' unless mailLines
          mails << Mail.read_from_string(mailLines)
          mailLines =  nil
        elsif mailLines
          mailLines += line
        end
      end
      mails
    end

  end

end

