from gds.app import app
import os
from datetime import datetime
from flask import Flask, flash, request, redirect, render_template, send_file, Response, jsonify
from werkzeug.utils import secure_filename
import cv2 as cv
import numpy as np
import io
from PIL import Image, ImageChops
import base64
from gds.Helpers import *
from numpy.core.fromnumeric import argmax, argmin

ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])
imgWidth = 1280
imgHeight = 720


def allowed_file(filename):
	return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/contact')
def contact_page():
    return 'This is the contact page'

@app.route('/contact/message', methods=['POST'])
def contact_message():
    if request.method == 'POST':
        message = request.form.get('message')
        return jsonify({'message': message})


@app.route('/')
def upload_form():
	return render_template('upload.html')

def increase_brightness(img, value=30):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)

    lim = 255 - value
    v[v > lim] = 255
    v[v <= lim] += value

    final_hsv = cv2.merge((h, s, v))
    img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
    return img

def increase_contrast(img, clipLimitValue=2, tileGridSizeValue=(16, 16)):
	lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
	l_channel, a, b = cv2.split(lab)
	clahe = cv2.createCLAHE(clipLimit=clipLimitValue, tileGridSize=tileGridSizeValue)
	cl = clahe.apply(l_channel)
	limg = cv2.merge((cl, a, b))
	return cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)

def trim_whitespace(img, grayValue = 32):
	gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
	gray = 255 * (gray < grayValue).astype(np.uint8)
	coords = cv2.findNonZero(gray)
	x, y, w, h = cv2.boundingRect(coords)
	img = img[y:y + h, x:x + w]
	return img

def trim(im):
    bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
    diff = ImageChops.difference(im, bg)
    diff = ImageChops.add(diff, diff, 2.0, -100)
    bbox = diff.getbbox()
    if bbox:
        return im.crop(bbox)


def clahe(img, clip_limit=2.0, grid_size=(8,8)):
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
    return clahe.apply(img)


def preProcessing(img):
	grayedImg = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
	blurredImg = cv.GaussianBlur(grayedImg, (5, 5), 1)
	canniedImg = cv.Canny(blurredImg, 200, 80)
	kernel = np.ones((5, 5))
	dilatedImg = cv.dilate(canniedImg, kernel, iterations=2)
	thresImg = cv.erode(dilatedImg, kernel, iterations=1)

	return canniedImg;


def getContours(img, imgContour):
	biggest = np.array([])
	maxArea = 0
	contours, hierarchy = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
	for cnt in contours:
		area = cv.contourArea(cnt)
		if area > 5000:
			# Drawing the shapes that were detected
			# syntax, drawContours(imgSrc,presentIterationNo,-1=for all detected contours,color,thickness)
			# cv.drawContours(imgContour,cnt,-1,(255,0,0),2)

			# Calculation of perimeter
			# syntax, srcLength(presentContour,isItClosedContour)
			peri = cv.arcLength(cnt, True)

			# Aproximating the no of cornor point
			# syntax, approxPolyDP(presntContour,resolution,isClosed)
			approx = cv.approxPolyDP(cnt, 0.02 * peri, True)
			if area > maxArea and len(approx) == 4:
				biggest = approx
				maxArea = area
	cv.drawContours(imgContour, biggest, -1, (255, 0, 0), 20)
	return biggest;


def reOrderPts(myPoints):
	myPoints = myPoints.reshape((4, 2))
	myNewPoints = np.zeros((4, 1, 2), np.int32)
	add = myPoints.sum(1)

	myNewPoints[0] = myPoints[argmin(add)]
	myNewPoints[3] = myPoints[argmax(add)]

	diff = np.diff(myPoints, axis=1);
	myNewPoints[1] = myPoints[argmin(diff)]
	myNewPoints[2] = myPoints[argmax(diff)]

	return myNewPoints


def getWarped(img, biggest):
	biggest = reOrderPts(biggest)
	# define the points for which we wanting to change the perspective
	pts1 = np.float32(biggest)
	# define the points about which we are going to change the view
	pts2 = np.float32([[0, 0], [imgWidth, 0], [0, imgHeight], [imgWidth, imgHeight]])

	# generate the transformation matrix for warping it
	matrix = cv.getPerspectiveTransform(pts1, pts2)
	print(matrix)

	# now warping the image using the transformation matrix and deine resolution
	imgOutput = cv.warpPerspective(img, matrix, (imgWidth, imgHeight))

	return imgOutput


#v3

# finds the edges of the image
def getContoursv3(img):
	biggest = np.array([])
	maxArea = 0
	contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
	for cnt in contours:
		area = cv2.contourArea(cnt)
		if area > 5000:
			peri = cv2.arcLength(cnt, True)
			approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
			if area > maxArea and len(approx) == 4:
				biggest = approx
				maxArea = area
	return biggest


# the image is preprocessed by applying different filters, to find the edges and the text
def preProcessingv3(img):
	imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # turning into gray scale image
	imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)  # adding gausian blur
	# thres = valTrackbarsv3()  # getting track bar values
	# imgCanny = cv2.Canny(imgBlur, thres[0], thres[1])  # canny blur
	imgCanny = cv2.Canny(imgBlur, 200, 80)
	kernel = np.ones((5, 5))
	imgDial = cv2.dilate(imgCanny, kernel, iterations=1)  # applying dilation
	imgErode = cv2.erode(imgDial, kernel, iterations=1)  # applying erosion
	return imgErode


# calculates the four courner points of the image
def reorderv3(myPoints):
	myPoints = myPoints.reshape((4, 2))
	myPointsNew = np.zeros((4, 1, 2), np.int32)
	add = myPoints.sum(1)
	myPointsNew[0] = myPoints[np.argmin(add)]
	myPointsNew[3] = myPoints[np.argmax(add)]
	diff = np.diff(myPoints, axis=1)
	myPointsNew[1] = myPoints[np.argmin(diff)]
	myPointsNew[2] = myPoints[np.argmax(diff)]
	return myPointsNew


# the image is cropped at the edges, with the obtained four points(co-ordinates)
def getWarpv3(img, biggest):
	biggest = reorderv3(biggest)
	pts1 = np.float32(biggest)
	pts2 = np.float32([[0, 0], [imgWidth, 0], [0, imgHeight], [imgWidth, imgHeight]])
	matrix = cv2.getPerspectiveTransform(pts1, pts2)
	imgOutput = cv2.warpPerspective(img, matrix, (imgWidth, imgHeight))
	imgOutput = imgOutput[5:img.shape[0] - 5, 5:img.shape[1] - 5]
	imgOutput = cv2.resize(imgOutput, (1280, 720))
	return imgOutput





@app.route('/uploaddoc', methods=['POST', 'GET'])
def upload_image():

	if 'file' not in request.files:
		flash('No file part')
		return redirect(request.url)

	file = request.files['file']
	if file.filename == '':
		flash('No image selected for uploading')
		return redirect(request.url)
	if file and allowed_file(file.filename):
		flash('Document scan was successful')
		filename = secure_filename(file.filename)

		filestr = request.files['file'].read()
		npimg = np.frombuffer(filestr, np.uint8)
		image = cv2.imdecode(npimg, cv2.IMREAD_UNCHANGED)
		ratio = image.shape[0] / 500.0
		orig = image.copy()
		image = Helpers.resize(image, height = 500)

		gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
		gray = cv2.GaussianBlur(gray, (5, 5), 0)
		edged = cv2.Canny(gray, 75, 200)

		cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
		cnts = Helpers.grab_contours(cnts)
		cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]

		for c in cnts:
			peri = cv2.arcLength(c, True)
			approx = cv2.approxPolyDP(c, 0.02 * peri, True)
			if len(approx) == 4:
				screenCnt = approx
				break

		try:
			cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
		except:
			return "image error"

		warped = Helpers.transform(orig, screenCnt.reshape(4, 2) * ratio)

		img = cv2.cvtColor(warped, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64img = "data:image/png;base64,"+base64.b64encode(file_object.getvalue()).decode('ascii')
		# return render_template('upload.html', image=base64img )
		base64og = base64.b64encode(file_object.getvalue()).decode('ascii')
		# return base64og
		image_data = base64.b64decode(base64img.split(',')[1])

		image_buffer = io.BytesIO()
		image_buffer.write(image_data)
		image_buffer.seek(0)

		image = Image.open(image_buffer).convert('RGB')

		width, height = image.size

		open_cv_image = np.array(image)
		open_cv_image = open_cv_image[:, :, ::-1].copy()

		# Crop padding
		paddingValue = 8
		cropped_image = open_cv_image[paddingValue:height - paddingValue, paddingValue:width - paddingValue]

		# Increase brightness
		enhanced_img = increase_brightness(cropped_image, value=50)

		# Increase contrast
		enhanced_img = increase_contrast(enhanced_img, tileGridSizeValue=(20, 20))

		# Trim white space
		enhanced_img = trim_whitespace(enhanced_img, grayValue=80)

	    # Convert cv2 image to bytes
		# img_encode = cv2.imencode('.png', enhanced_img)[1]
		# data_encode = np.array(img_encode)
		# byte_encode = data_encode.tobytes()

		img = cv2.cvtColor(enhanced_img, cv2.COLOR_BGR2RGB)
		file_downloading = io.BytesIO()
		pilImageDownloading = Image.fromarray(img, 'RGB')
		pilImageDownloading.save(file_downloading, 'PNG')
		base64Download = base64.b64encode(file_downloading.getvalue()).decode('ascii')

		#result = base64.b64encode(byte_encode)
		#return result

		return base64Download

	else:
		return "'Allowed image types are -> png, jpg, jpeg'"

@app.route('/uploaddocv2', methods=['GET', 'POST'])
def uploaddocv2():
	if 'file' not in request.files:
		flash('No file part')
		return redirect(request.url)

	file = request.files['file']
	if file.filename == '':
		flash('No image selected for uploading')
		return redirect(request.url)
	if file and allowed_file(file.filename):
		flash('Document scan was successful')
		filestr = request.files['file'].read()

		npimg = np.frombuffer(filestr, np.uint8)
		image = cv2.imdecode(npimg, cv2.IMREAD_UNCHANGED)

		imgContour = Helpers.resize(image, height=500)

		thresImg = preProcessing(image)
		biggest = getContours(thresImg, imgContour)
		print(biggest);
		warpedImg = getWarped(image, biggest)

		img = cv2.cvtColor(warpedImg, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64img = "data:image/png;base64," + base64.b64encode(file_object.getvalue()).decode('ascii')
		image_data = base64.b64decode(base64img.split(',')[1])


		image_buffer = io.BytesIO()
		image_buffer.write(image_data)
		image_buffer.seek(0)

		image = Image.open(image_buffer).convert('RGB')
		# image = Image.open(file_object).convert('RGB')

		width, height = image.size

		open_cv_image = np.array(image)
		open_cv_image = open_cv_image[:, :, ::-1].copy()

		# Crop padding
		paddingValue = 5
		cropped_image = open_cv_image[paddingValue:height - paddingValue, paddingValue:width - paddingValue]

		# Increase brightness
		enhanced_img = increase_brightness(cropped_image, value=50)

		# Increase contrast
		enhanced_img = increase_contrast(enhanced_img, tileGridSizeValue=(20, 20))

		# Trim white space
		enhanced_img = trim_whitespace(enhanced_img, grayValue=80)

		img = cv2.cvtColor(enhanced_img, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64og = base64.b64encode(file_object.getvalue()).decode('ascii')
		return base64og
		
@app.route('/uploaddocvV', methods=['GET', 'POST'])
def uploaddocvV():
	if 'file' not in request.files:
		flash('No file part')
		return redirect(request.url)

	file = request.files['file']
	if file.filename == '':
		flash('No image selected for uploading')
		return redirect(request.url)
	if file and allowed_file(file.filename):
		flash('Document scan was successful')
		filestr = request.files['file'].read()

		npimg = np.frombuffer(filestr, np.uint8)
		image = cv2.imdecode(npimg, cv2.IMREAD_UNCHANGED)

        
		imgContour = Helpers.resize(image, height=500)

		thresImg = preProcessing(image)
		biggest = getContours(thresImg, imgContour)
		print(biggest);
		warpedImg = getWarped(image, biggest)
		
		rotated_img = cv2.rotate(warpedImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
		img = cv2.cvtColor(rotated_img, cv2.COLOR_BGR2RGB)
# 		img = cv2.cvtColor(warpedImg, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64img = "data:image/png;base64," + base64.b64encode(file_object.getvalue()).decode('ascii')
		image_data = base64.b64decode(base64img.split(',')[1])


		image_buffer = io.BytesIO()
		image_buffer.write(image_data)
		image_buffer.seek(0)

		image = Image.open(image_buffer).convert('RGB')
		# image = Image.open(file_object).convert('RGB')

		width, height = image.size

		open_cv_image = np.array(image)
		open_cv_image = open_cv_image[:, :, ::-1].copy()

		# Crop padding
		paddingValue = 5
		cropped_image = open_cv_image[paddingValue:height - paddingValue, paddingValue:width - paddingValue]

		# Increase brightness
		enhanced_img = increase_brightness(cropped_image, value=50)

		# Increase contrast
		enhanced_img = increase_contrast(enhanced_img, tileGridSizeValue=(20, 20))

		# Trim white space
		enhanced_img = trim_whitespace(enhanced_img, grayValue=80)

		img = cv2.cvtColor(enhanced_img, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64og = base64.b64encode(file_object.getvalue()).decode('ascii')
		return base64og



@app.route('/uploaddocv3', methods=['GET', 'POST'])
def uploaddocv3():
	if 'file' not in request.files:
		flash('No file part')
		return redirect(request.url)

	file = request.files['file']
	if file.filename == '':
		flash('No image selected for uploading')
		return redirect(request.url)
	if file and allowed_file(file.filename):
		flash('Document scan was successful')
		filestr = request.files['file'].read()
		npimg = np.frombuffer(filestr, np.uint8)
		image = cv2.imdecode(npimg, cv2.IMREAD_UNCHANGED)


		imgContour = Helpers.resize(image, height=500)
		imgProcessed = preProcessingv3(image)
		biggest = getContoursv3(imgProcessed)
		print("biggest contours: " + str(biggest.size))
		if biggest.size != 0:
			imgWarp = getWarpv3(image, biggest)
		img = cv2.cvtColor(imgWarp, cv2.COLOR_BGR2RGB)
		file_object = io.BytesIO()
		img = Image.fromarray(img)
		img.save(file_object, 'PNG')
		base64img = "data:image/png;base64," + base64.b64encode(file_object.getvalue()).decode('ascii')
		# return render_template('upload.html', image=base64img )
		base64og = base64.b64encode(file_object.getvalue()).decode('ascii')
		return base64og

if __name__ == "__main__":
    app.run(debug=True)