#!/usr/bin/env python
import os
import os.path
import sys
import shutil
from subprocess import call
import importlib
import unittest
import re

from testcases import settings

class Workspace(object):
  
  def __init__(self):
    self.root_dir = os.getcwd()
    self.build_dir = os.path.join(self.root_dir,"tmpbin");
    self.log_dir = os.path.join(self.root_dir,"logs");
    self.tests_dir = os.path.join(self.root_dir,"testcases");
    self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples")
    self.examples = []
    self.tests = []
    if not os.path.isdir("../PubSubClient"):
      raise Exception("Cannot find PubSubClient library")
    try:
      import ino
    except:
      raise Exception("ino tool not installed")

  def init(self):
    if os.path.isdir(self.build_dir):
      shutil.rmtree(self.build_dir)
    os.mkdir(self.build_dir)
    if os.path.isdir(self.log_dir):
      shutil.rmtree(self.log_dir)
    os.mkdir(self.log_dir)
    
    os.chdir(self.build_dir)
    call(["ino","init"])
    
    shutil.copytree("../../PubSubClient","lib/PubSubClient")
    
    filenames = []
    for root, dirs, files in os.walk(self.examples_dir):
      filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
    filenames.sort()
    for e in filenames:
      self.examples.append(Sketch(self,e))
    
    filenames = []
    for root, dirs, files in os.walk(self.tests_dir):
      filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
    filenames.sort()
    for e in filenames:
      self.tests.append(Sketch(self,e))
      
  def clean(self):
    shutil.rmtree(self.build_dir)
  
class Sketch(object):
  def __init__(self,wksp,fn):
    self.w = wksp
    self.filename = fn
    self.basename = os.path.basename(self.filename)
    self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),))
    self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),))
    self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),))

  def build(self):
    sys.stdout.write(" Build:   ")
    sys.stdout.flush()
    
    # Copy sketch over, replacing IP addresses as necessary
    fin = open(self.filename,"r")
    lines = fin.readlines()
    fin.close()
    fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w")
    for l in lines:
      if re.match(r"^byte server\[\] = {",l):
        fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),))
      elif re.match(r"^byte ip\[\] = {",l):
        fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),))
      else:
        fout.write(l)
    fout.flush()
    fout.close()
    
    # Run build
    fout = open(self.build_log, "w")
    ferr = open(self.build_err_log, "w")
    rc = call(["ino","build"],stdout=fout,stderr=ferr)
    fout.close()
    ferr.close()
    if rc == 0:
      sys.stdout.write("pass")
      sys.stdout.write("\n")
      return True
    else:
      sys.stdout.write("fail")
      sys.stdout.write("\n")
      with open(self.build_err_log) as f:
        for line in f:
          print " ",line,
      return False
  
  def upload(self):
    sys.stdout.write(" Upload:  ")
    sys.stdout.flush()
    fout = open(self.build_upload_log, "w")
    rc = call(["ino","upload"],stdout=fout,stderr=fout)
    fout.close()
    if rc == 0:
      sys.stdout.write("pass")
      sys.stdout.write("\n")
      return True
    else:
      sys.stdout.write("fail")
      sys.stdout.write("\n")
      with open(self.build_upload_log) as f:
        for line in f:
          print " ",line,
      return False


  def test(self):
    # import the matching test case, if it exists
    try:
      basename = os.path.basename(self.filename)[:-4]
      i = importlib.import_module("testcases."+basename)
    except:
      sys.stdout.write(" Test:    no tests found")
      sys.stdout.write("\n")
      return
    c = getattr(i,basename)
    
    testmethods = [m for m in dir(c) if m.startswith("test_")]
    testmethods.sort()
    tests = []
    for m in testmethods:
      tests.append(c(m))
      
    result = unittest.TestResult()
    c.setUpClass()
    if self.upload():
      sys.stdout.write(" Test:    ")
      sys.stdout.flush()
      for t in tests:
        t.run(result)
        print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun)
        if not result.wasSuccessful():
          if len(result.failures) > 0:
            for f in result.failures:
              print "-- %s"%(str(f[0]),)
              print f[1]
          if len(result.errors) > 0:
            print " Errors:"
            for f in result.errors:
              print "-- %s"%(str(f[0]),)
              print f[1]
    c.tearDownClass()

if __name__ == '__main__':
  run_tests = True

  w = Workspace()
  w.init()
  
  for e in w.examples:
    print "--------------------------------------"
    print "[%s]"%(e.basename,)
    if e.build() and run_tests:
      e.test()
  for e in w.tests:
    print "--------------------------------------"
    print "[%s]"%(e.basename,)
    if e.build() and run_tests:
      e.test()
  
  w.clean()
