#!/usr/bin/env python
"""Small webserver to browse C/C++ source code and see the CCM for each file
"""
from __future__ import with_statement
import BaseHTTPServer
import cgi, sys
import os
import time
import socket
import urllib
import urlparse
import cgi
from time import mktime, strptime
import tempfile
import subprocess
from calculate_ccm import calculate_ccm

if len(sys.argv)<2 or len(sys.argv)>3:
    print "Usage: %s <port> [base_dir]"%sys.argv[0]
    sys.exit(99)

server_port=int(sys.argv[1])
if len(sys.argv)>=3:
    base_dir = sys.argv[2]
else:
    base_dir=os.getcwd()

handled_extension=["cc","c","cpp","h","hpp"]

class CCMCache:
    def __init__(self,dir_path):
        self.cache_file_name = dir_path+"/.ccm_cache"
        self.dir_path = dir_path
        self.entries={}
        self.modified=False
        
        if os.path.exists(self.cache_file_name):
            with open(self.cache_file_name) as cache_file:
                for l in cache_file.readlines():
                    ls=l.split(',')
                    if len(ls)<4:
                        continue
                    file_name=ls[0]
                    timestamp=ls[1]
                    ccm=int(ls[2])
                    lines=int(ls[3])
                    #validate entry
                    full_file_name=dir_path+"/"+file_name
                    if os.path.exists(full_file_name):
                        if os.path.getmtime(full_file_name)<=timestamp:
                            self.entries[file_name]=(timestamp,ccm,lines)
    
    def getMetrics(self,file_name):
        if not file_name in self.entries:
            full_file_name=self.dir_path+"/"+file_name
            tmp_metrics=calculate_ccm(full_file_name)
            self.entries[file_name]=(os.path.getmtime(full_file_name),tmp_metrics[0],tmp_metrics[1])
            self.modified=True
        return self.entries[file_name][1:3]

    def saveIfChanged(self):
        if self.modified:
            with open(self.cache_file_name,"w") as file_handle:
                for i in self.entries.iteritems():
                    print >>file_handle, "%s,%s,%d,%d"%(i[0],i[1][0],i[1][1],i[1][2])

def loadCCMCache(full_dir_path):
    return CCMCache(full_dir_path)
def unloadCCMCache(ccm_cache):
    ccm_cache.saveIfChanged()


class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        parsed_url = urlparse.urlparse(self.path)
        parameters = cgi.parse_qs(parsed_url.query)
        full_path=base_dir+"/"+parsed_url.path
        full_path=full_path.rstrip('/')
        if parsed_url.path.find("..")!=-1:
            self.serve_not_found(full_path,parsed_url.path)
        elif os.path.isdir(full_path):
            self.serve_dir(full_path,parsed_url.path)
        elif os.path.isfile(full_path):
            self.serve_file(full_path,parsed_url.path)
        elif not os.path.exists(full_path):
            self.serve_not_found(full_path,parsed_url.path)
        else:
            self.serve_internal_error(full_path,parsed_url.path)
    
    def serve_file(self,full_path,relative_path):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        with open(full_path,"rb",1000000) as f:
            while True:
                b=f.read(1000000);
                if b==None or len(b)==0: break
                self.wfile.write(b)
    
    def serve_dir(self,full_path,relative_path):
        dir_contents=os.listdir(full_path)
        dir_contents.sort()
        
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        
        print >>self.wfile, '<?xml version="1.0" encoding="iso-8859-1"?>'
        print >>self.wfile, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
        print >>self.wfile, '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'
        print >>self.wfile, '<head>'
        print >>self.wfile, '  <title>%s - file list</title>'%relative_path
        print >>self.wfile, '</head>'
        print >>self.wfile, '<body>'
        print >>self.wfile, '<h1>%s</h1>'%relative_path
        print >>self.wfile, '<table>'
        print >>self.wfile, '  <tr>'
        print >>self.wfile, '    <th>Name</th>'
        print >>self.wfile, '    <th>CCM</th>'
        print >>self.wfile, '    <th>Lines</th>'
        print >>self.wfile, '    <th>CCM/lines ratio</th>'
        print >>self.wfile, '  </tr>'
        
        ccm_cache=loadCCMCache(full_path)
        
        #parent dir - don't go above base
        if relative_path!="/" and relative_path!="":
            relative_parent_dir=relative_path.rsplit('/',2)[0]
            if relative_parent_dir=="" or relative_parent_dir[-1]!="/":
                relative_parent_dir+="/"
            print >>self.wfile, '  <tr><td colspan="4"><a href="%s">&lt;Parent directory&gt;</a></td></tr>'%(relative_parent_dir)
        
        total_ccm=0
        total_lines=0;
        for file_name in dir_contents:
            if file_name[0]=='.':
                #Hide names that start with a dot.
                continue
            
            full_file_name=full_path+"/"+file_name
            if os.path.isdir(full_file_name):
                print >>self.wfile, '  <tr><td colspan="4"><a href="%s">%s</a></td></tr>'%(file_name+"/",file_name+"/")
            else:
                if len(file_name.rsplit('.',1))<2:
                    continue
                extension=file_name.rsplit('.',1)[1]
                if extension in handled_extension:
                    print >>self.wfile, '  <tr>'
                    print >>self.wfile, '    <td><a href="%s">%s</a></td>'%(file_name,file_name)
                    metrics=ccm_cache.getMetrics(file_name)
                    if metrics!=None:
                        print >>self.wfile, '    <td>%d</td><td>%d</td><td>%.2f</td>'%(metrics[0],metrics[1],metrics[0]*1.0/metrics[1])
                        total_ccm += metrics[0]
                        total_lines += metrics[1]
                    print >>self.wfile, '  </tr>'
                else:
                    pass

        if total_lines!=0:
            print >>self.wfile, '  <tr><td><b>total</b></td><td>%d</td><td>%d</td><td>%.2f</td></tr>'%(total_ccm,total_lines,total_ccm*1.0/total_lines)

        print >>self.wfile, '</table>'
        print >>self.wfile, '</body>'
        print >>self.wfile, '</html>'
        
        unloadCCMCache(ccm_cache)
    
    def serve_not_found(self,full_path,relative_path):
        self.send_response(404)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        print >>self.wfile, '<?xml version="1.0" encoding="iso-8859-1"?>'
        print >>self.wfile, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
        print >>self.wfile, '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'
        print >>self.wfile, '<head>'
        print >>self.wfile, '  <title>%s - 404 Not found</title>'%relative_path
        print >>self.wfile, '</head>'
        print >>self.wfile, '<body>'
        print >>self.wfile, '  <h1>%s - 404 Not found</h1>'%relative_path
        print >>self.wfile, '  <p>Could not find %s</p>'%relative_path
        print >>self.wfile, '</body>'
        print >>self.wfile, '</html>'
    
    def serve_internal_error(self,full_path,relative_path):
        self.send_response(500)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        print >>self.wfile, '<?xml version="1.0" encoding="iso-8859-1"?>'
        print >>self.wfile, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
        print >>self.wfile, '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'
        print >>self.wfile, '<head>'
        print >>self.wfile, '  <title>%s - 500 Internal Server Error</title>'%relative_path
        print >>self.wfile, '</head>'
        print >>self.wfile, '<body>'
        print >>self.wfile, '  <h1>%s - 500 Internal Server Error</h1>'%relative_path
        print >>self.wfile, '  <p>Don''t know what %s is</p>'%relative_path
        print >>self.wfile, '</body>'
        print >>self.wfile, '</html>'
    
    

        

httpd = BaseHTTPServer.HTTPServer(("", server_port), Handler)
httpd.serve_forever()
