import bottle
import sqlite3
import string
from urllib.parse import urlparse, urlunparse, ParseResult

app = bottle.Bottle()
config = {}

ALPHABET = string.digits + string.ascii_letters
TABLE = 'shorturl'
COLUMN_ID = '_id'
COLUMN_URL = 'url'

@app.route('/')
def index():
    return bottle.static_file('form.html', 'static')

@app.route('/url/submit', method='POST')
def url_submit():
    original_url = bottle.request.forms.url
    url = sanitize_url(original_url)
    if url == '' or url is None:
        bottle.abort(500, 'Error while trying to shorten {}'.format(original_url))
    print(url)
    shortstring = add_url(url)
    if shortstring is None:
        bottle.abort(500, 'Error while trying to shorten {}'.format(original_url))
    else:
        return 'Shortened {0}: <a href="/{1}">{2}{1}</a>'.format(url, shortstring, config['shortlink_prefix'])

@app.route('/<shortstring:re:[a-zA-Z0-9]+>')
def view_url(shortstring):
    url = get_url(shortstring)
    if url is None:
        bottle.abort(404, 'There is no place like "{}"'.format(shortstring))
    else:
        bottle.redirect(url, 301)

def get_db_connection():
    return sqlite3.connect(config['database'])

def init_database():
    conn = get_db_connection()
    init_database_with_conn(conn)
    conn.close()

def init_database_with_conn(conn):
    c = conn.cursor()
    c.execute("""CREATE TABLE IF NOT EXISTS {} (
                {} INTEGER PRIMARY KEY AUTOINCREMENT,
                {} TEXT
             );""".format(TABLE, COLUMN_ID, COLUMN_URL))
    conn.commit()

def shortstring_for_id(url_id):
    if url_id < 0:
        return None

    shortstring = '' if url_id != 0 else ALPHABET[0]
    while url_id != 0:
        shortstring = ALPHABET[url_id % len(ALPHABET)] + shortstring
        url_id //= len(ALPHABET)
    return shortstring

def id_for_shortstring(shortstring):
    if len(shortstring) > 1 and shortstring[0] == '0':
        return None
    elif len(shortstring) < 1:
        return None

    url_id = 0
    for i, c in enumerate(shortstring):
        try:
            value = ALPHABET.index(c)
        except ValueError:
            return None
        url_id += value * len(ALPHABET) ** (len(shortstring) - i - 1)
    return url_id

def add_url(url):
    conn = get_db_connection()
    shortstring = add_url_with_conn(conn, url)
    conn.close()
    return shortstring

def add_url_with_conn(conn, url):
    c = conn.cursor()
    c.execute("INSERT INTO {} ({}) VALUES(?)".format(TABLE, COLUMN_URL), (url,))
    conn.commit()
    return shortstring_for_id(c.lastrowid)

def get_url(shortstring):
    conn = get_db_connection()
    url = get_url_with_conn(conn, shortstring)
    conn.close()
    return url

def get_url_with_conn(conn, shortstring):
    c = conn.cursor()
    c.execute("SELECT {} FROM {} WHERE {} = ?".format(COLUMN_URL, TABLE, COLUMN_ID), (id_for_shortstring(shortstring),))
    row = c.fetchone()
    if row is None:
        return None
    else:
        return row[0]

def sanitize_url(url):
    parsed_url = urlparse(url)
    if parsed_url.netloc == '':
        if parsed_url.path == '':
            return None
        else:
            parsed_url = ParseResult(parsed_url.scheme, parsed_url.path, '', parsed_url.params, parsed_url.query, parsed_url.fragment)
    if parsed_url.scheme == '':
        parsed_url = ParseResult("http", parsed_url.netloc, parsed_url.path, parsed_url.params, parsed_url.query, parsed_url.fragment)
    elif parsed_url.scheme == 'javascript':
        return None
    return urlunparse(parsed_url)

if __name__ == '__main__':
    config['database'] = 'bottleshort.db'
    config['shortlink_prefix'] = 'http://localhost:8080/'
    init_database()
    bottle.run(app, host='localhost', port=8080, debug=True)
