#!/usr/bin/env python
# -*- coding: utf-8 -*-

import glob
import argparse
import re
import locale
import subprocess
from subprocess import PIPE
import sys
import os
import time
import json
import sqlite3
from googletrans import Translator 
import urllib2
from mtranslate.core import translate
from bs4 import BeautifulSoup
from selenium import webdriver
import shutil
from shutil import copyfile
sys.path.append("../../apicog_py")

from datainitializer.ScreenApiFlow import ScreenApiFlow
from datainitializer.ImageString import ImageString
from config.constant import Values


def record_statisitic():
	sens_apis = set([])
	if os.path.isfile(Values.record_pkgs_path):
		os.remove(Values.record_pkgs_path)
	if os.path.isfile(Values.record_pkgs_dump_path):
		os.remove(Values.record_pkgs_dump_path)

	record_count =0
	json_record = {}

	for dirpath in Values.dy_result_paths:
		for pkgname in os.listdir(dirpath):
			hasRecord = False
			pkgdir = os.path.join(dirpath, pkgname)
			app_droidbot = os.path.join(pkgdir, 'droidbot') 
			api_record = os.path.join(pkgdir, 'api_record.txt')
			api_record_map = {}
			if os.path.isfile(api_record):
				os.remove(api_record)
			if os.path.isdir(app_droidbot):
				logcatfile = os.path.join(app_droidbot, 'logcat.txt')
				if os.path.isfile(logcatfile):
					lines = set(line.strip() for line in open(logcatfile))
					for ln in lines:
						if 'jie-record' in ln: #and 'android.content.ContentResolver.query' in ln:
							recordFile = open(api_record, 'a')
							jie_record = ln.split('jie-record: ')[-1]
							recordFile.write('%s\n' % (jie_record))
							recordFile.close()					
							hasRecord = True

							sens_apis.add(jie_record.split(' ++ ')[1])
							sub_record = ' ++ '.join(jie_record.split(' ++ ')[:-2])
							times = jie_record.split(' ++ ')[-2]

							if sub_record not in api_record_map:
								api_record_map[sub_record] = jie_record
							else:
								old_time = api_record_map[sub_record].split(' ++ ')[-2]
								if int(old_time)<int(times):
									api_record_map[sub_record] = jie_record
			if hasRecord:
				print pkgdir
				record_count +=len(api_record_map)
				recordFiles = open(Values.record_pkgs_path, 'a')
				recordFiles.write('%s\n' % (pkgdir))
				for record in api_record_map.values():
					recordFiles.write('%s\n' % (record))
				recordFiles.close()	
				json_record[pkgdir]	= api_record_map


	with open(Values.record_pkgs_dump_path, 'w') as outfile:
		json.dump(json_record, outfile)

	for api in sens_apis:
		print api

	print("sens_apis size: " + str(len(sens_apis)))
	print("record count: " + str(record_count))

def record_json_load():
	with open(Values.record_pkgs_dump_path, 'r') as input_file:
		content = json.load(input_file)

	record_count = 0
	for value in content.values():
		record_count += len(value)
		print len(value)
	print("record count: " + str(record_count))
	print("record pkgs: " + str(len(content.keys())))

def get_full_name_of_aty(atyname):
	pkgname, atyname = atyname.split('/')
	if atyname.startswith('.'):
		atyname = pkgname+atyname
	return pkgname,atyname

def get_api_trace(app_api_db,hashcode,topaty,mtdsig):
    db_connection = sqlite3.connect(app_api_db)
    stacktrace = db_connection.execute('select stacktrace from apitrace where hascode = ? AND mtdsig = ?', (hashcode,mtdsig,)).fetchone()
    
    if stacktrace is None:
    	print app_api_db,hashcode,topaty,mtdsig
    	return None
    return stacktrace[0].encode('utf-8')

def byteify(input, encoding='utf-8'):
    if isinstance(input, dict):
        return {byteify(key): byteify(value) for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode(encoding)
    else:
        return input


def get_screen_api_flow():
	if os.path.isfile(Values.screen_api_flow_no_screen_path):
		os.remove(Values.screen_api_flow_no_screen_path)

	db_pipeline = ScreenApiFlow(Values.screen_api_flow_db)
	db_pipeline.initialize()

	with open(Values.record_pkgs_dump_path, mode="rb") as input_file:
		json_data_dump = json.load(input_file)

	json_data_dump = byteify(json_data_dump)

	for appdir, revaule in json_data_dump.iteritems():
		records = revaule.values()
		print appdir
		# if 'malware' in appdir:
		# 	continue
		app_droidbot_states = os.path.join(appdir, 'droidbot/states')

		if not os.path.isdir(app_droidbot_states):
			continue

		json_paths = glob.glob(os.path.join(app_droidbot_states, '*.json'))
		time_to_image = {}
		for json_path in json_paths:
			with open(json_path, 'r') as input_file:
				image_json = json.load(input_file)
			image_json = byteify(image_json)

			image_name = json_path.split('/')[-1].replace('state','screen')
			image_name = image_name[:-4]+'jpg'
			image_path = os.path.join('/'.join(image_name.split('/')[:-1]), image_name)

			imageaty = image_json['foreground_activity']
			pkgname,full_name_aty = get_full_name_of_aty(imageaty)
			time = image_json['tag'].replace('-','').replace('_','')[4:]
			time_to_image[time] = {'screenimagepath':image_path, 'screenpkgname':pkgname, 'screenaty':full_name_aty}

		null_reuslt = list()
		for record in records:
			topaty,senapi,hashcode,counts,time = record.split(' ++ ')
			app_api_db = os.path.join(appdir, 'sapiguard.db') 
			stacktrace = get_api_trace(app_api_db,hashcode,topaty,senapi)
			screenapiflowitem = {'appresultpath':appdir, 'topaty':topaty, 'senapi':senapi, 'hashcode':hashcode, 'stacktrace':stacktrace, 'count':counts, 'label':None, 'solved':None}
			if topaty == 'com.android.launcher2.Launcher':
				screenapiflowitem['screenaty'] = 'com.android.launcher2.Launcher'
				screenapiflowitem['screenpkgname'] = 'com.sec.android.app.launcher'
				screenapiflowitem['screenimagepath'] = None
				db_pipeline.process_item(screenapiflowitem)
				continue

			nearest_time = '0'
			for t in time_to_image.keys():
				if int(time) > int(t) and abs(int(time) -int(t)) < abs(int(time) - int(nearest_time)):
					if topaty == time_to_image[t]['screenaty'] or 'com.google.android.packageinstaller' == time_to_image[t]['screenpkgname']:
						nearest_time = t
			
			if nearest_time == '0':
				if abs(int(time) -int(t)) < abs(int(time) - int(nearest_time)):
					if topaty == time_to_image[t]['screenaty'] or 'com.google.android.packageinstaller' == time_to_image[t]['screenpkgname']:
						nearest_time = t

			if nearest_time == '0':
				# if topaty== time_to_image[nearest_time]['imageaty']:
				null_reuslt.append(record)
				# exit(0)
			else:
				screenapiflowitem.update(time_to_image[nearest_time])
				db_pipeline.process_item(screenapiflowitem)

				# result_flow = [topaty,senapi,hashcode,counts,pkgname,time_to_image[nearest_time]['imageaty'],time_to_image[nearest_time]['imagepath']]
				# print ' +++ '.join(result_flow)

		if null_reuslt:
			recordFile = open(Values.screen_api_flow_no_screen_path, 'a')
			recordFile.write('%s\n' % (appdir))
			recordFile.write('%s\n' % ('\n'.join(null_reuslt)))
			recordFile.close()

def isJavaLibraryClass(className):
    if className.startswith("java.") or className.startswith("sun.") \
            or className.startswith("javax.") or className.startswith("com.sun.") \
            or className.startswith("org.omg.") or className.startswith("org.xml.") \
            or className.startswith("org.w3c.dom"):
        return True

    return False

def isAppDeveloperCode(className):
    if className.startswith("com.android.locker") or \
            className.startswith("com.android.services") or \
            className.startswith("com.android.securing") or \
            className.startswith('com.android.lemon.radio') or \
            className.startswith('com.android.nmh'):
        return True
    return not (className.startswith("android.") or className.startswith("java.") \
            or className.startswith("com.android.") \
            or className.startswith("com.google.android") \
            or className.startswith("dalvik.system") \
            or className.startswith("biz.bokhorst.xprivacy") \
            or className.startswith("guard.api.sensitive") \
            or className.startswith("de.robv.android") \
            or className.startswith("org.chromium") \
            or className.startswith("com.samsung") \
            or isJavaLibraryClass(className))

def get_entry_callback(stacktrace):
	mtd_calls = stacktrace.encode('utf-8').split('\n')
	# for i,call in enumerate(reversed(mtd_calls)):
	for call in reversed(mtd_calls):
		if not call:
			continue

		if isAppDeveloperCode(call):
			# mtd = call.split('(')[0].split('.')[-1]
			# if not mtd.startswith('on') and not mtd.startswith('handleMessage') \
			# and not mtd.startswith('run') and not mtd.startswith('doInBackground') \
			# and not mtd.startswith('surfaceChanged') and mtd != 'call' \
			# and not mtd.startswith('loadInBackground'):
			return call.split('(')[0]
	print 'this stacktrace has no entry callback'
	return None


def stacktrace_compression():
	saf_db = ScreenApiFlow(Values.screen_api_flow_db)
	saf_db.initialize()
	
	appresultpaths = saf_db.get_appresultpaths()
	count = 0
	appresultpaths = set(appresultpaths)

	# for result_path in appresultpaths:
	# 	if 'fdroid'in result_path:
	# 		count+=1
	# print count
	# print len(appresultpaths)-count
	# exit(0)

	for result_path in appresultpaths:
		# if 'e9ef2d3002897f11e041e64863d47a4affbfdc5216f860fab51ae51447a5e23a' not in result_path:
		# 	continue

		flows = saf_db.get_flows_of_path(result_path)
		repeat_flows = set()
		for i in range(0, len(flows)):
			flow = flows[i]
			if flow in repeat_flows:
				continue
			stacktrace = flow[5]
			topaty = flow[2]
			senapi = flow[3]
			screenimagepath = flow[9]
			flow_count = int(flow[6])
			if not stacktrace:
				continue
			entry_callback = get_entry_callback(stacktrace)
			for j in range(i+1, len(flows)):
				follow_flow = flows[j]
				follow_stacktrace = follow_flow[5]
				if not follow_stacktrace:
					continue

				follow_topaty = follow_flow[2]
				follow_senapi = follow_flow[3]
				follow_screenimagepath = follow_flow[9]
				follow_entry_callback = get_entry_callback(follow_stacktrace)
				if entry_callback == follow_entry_callback and topaty == follow_topaty and \
					senapi == follow_senapi: #and screenimagepath == follow_screenimagepath:
					count+=1
					saf_db.delete_flow_by_id(follow_flow[0])
					flow_count +=int(follow_flow[6])
					saf_db.update_flow_count_by_id(flow[0], flow_count)
					print flow[0], follow_flow[0]
					repeat_flows.add(follow_flow)

	saf_db.finalize()
	print count


if __name__ == "__main__":
	# record_statisitic()
	# record_json_load()
	get_screen_api_flow()
	# stacktrace_compression()
