import math
import sys
import time
import random
import getpass
import os

import requests
from PIL import Image
from requests.adapters import HTTPAdapter

random.seed(time.time())



colors = [
    (255, 255, 255),
    (228, 228, 228),
    (136, 136, 136),
    (34, 34, 34),
    (255, 167, 209),
    (229, 0, 0),
    (229, 149, 0),
    (160, 106, 66),
    (229, 217, 0),
    (148, 224, 68),
    (2, 190, 1),
    (0, 211, 221),
    (0, 131, 199),
    (0, 0, 234),
    (207, 110, 228),
    (130, 0, 128)
]



def board_get_bitmap():
    "Fetch and store board as image"

    import urllib.request
    url = 'https://www.reddit.com/api/place/board-bitmap'
    response = urllib.request.urlopen(url, timeout=10)

    image = Image.new('P', (1000, 1000))
    # Set image color palette. PIL palettes have all colors
    # concatenated into one list: (255,255,255,228,228,228,...)
    image.putpalette(sum(colors, ()))
    pixels = image.load()

    for y in range(1000):
        for x in range(500):
            datum = ord(response.read(1))
            color1 = datum >> 4
            color2 = datum - (color1 << 4)
            pixels[x*2,     y] = color1
            pixels[x*2 + 1, y] = color2

    return image



def roll(image, delta):
    "Roll an image sideways"

    xsize, ysize = image.size

    delta = delta % xsize
    if delta == 0: return image

    part1 = image.crop((0, 0, delta, ysize))
    part2 = image.crop((delta, 0, xsize, ysize))
    image.paste(part2, (0, 0, xsize-delta, ysize))
    image.paste(part1, (xsize-delta, 0, xsize, ysize))

    return image



def get_differences(board, ref, offset):
    board_rgb = board.convert("RGB")
    (size_x, size_y) = ref.size
    board_img = board_rgb.crop((offset[0], offset[1], offset[0] + size_x, offset[1] + size_y))

    diff = []
    assert ref.mode == "RGBA"
    for i in range(size_x):
        for j in range(size_y):
            if ref.getpixel((i,j))[3] > 0:
                if ref.getpixel((i,j))[:3] != board_img.getpixel((i,j)):
                    diff.append((i,j))

    return diff



def find_palette(point):
    def distance(c1, c2):
        (r1, g1, b1) = c1
        (r2, g2, b2) = c2
        return math.sqrt((r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2)

    closest_colors = sorted(colors, key=lambda color: distance(color, point))
    closest_color = closest_colors[0]

    return colors.index(closest_color)



def place_pixel(ax, ay, new_color):
    print ("Probing pixel {},{}:".format(ax, ay))

    while True:
        request_url = "http://reddit.com/api/place/pixel.json?x={}&y={}"
        r = s.get(request_url.format(ax, ay), timeout=5)
        if r.status_code == 200:
            data = r.json()
            break
        else:
            print("ERROR: ", r, r.text)
        time.sleep(random.randint(5, 10))

    old_color = data["color"] if "color" in data else 0
    old_user_name = data["user_name"] if "user_name" in data else "<nobody>"

    if old_color == new_color:
        print("Skipping, color #{} set by {}".format(new_color, old_user_name))
        time.sleep(.25)
        return None
    else:
        print("Placing color #{}, ".format(new_color), end='')
        print("currently #{} by {}.".format(old_color, old_user_name))

        r = s.post("https://www.reddit.com/api/place/draw.json",
                   data={"x": str(ax), "y": str(ay), "color": str(new_color)})

        if "error" not in r.json():
            print("Placed color.")
        else:
            print("Could not place color: cooldown period?")

        return r



def place_image(ref, offset):
    "Place image ref at offset"

    print("Starting image placement:")
    print("Image of width {}, height {};".format(ref.size[0], ref.size[1]))
    print("Offset: {}.".format(offset))

    while True:
        print("Getting differential:")
        board = board_get_bitmap()
        roll(board, 8)
        diff = get_differences(board, ref, offset)

        if not diff:
            print("All done, sleeping.")
            time.sleep(random.randint(5,10))
        else:
            print("Got {} diffs.".format(len(diff)))
            random.shuffle(diff)

            while diff:
                point = diff[0]

                ax = offset[0] + point[0]
                ay = offset[1] + point[1]
                r = place_pixel(ax, ay, colors.index(ref.getpixel(point)[:3]))

                if r is not None:
                    if "error" not in r.json():
                        diff.remove(point)

                    wait_seconds = float(r.json()["wait_seconds"])
                    wait_time = int(wait_seconds) + 2
                    while (wait_time > 0):
                        msg = "Waiting {} seconds. Diffs left: {}.".format(wait_time, len(diff))
                        time.sleep(1)
                        wait_time -= 1
                        if wait_time > 0:
                            print(msg, end="              \r")
                        else:
                            print(msg)
                else:
                    diff.remove(point)



# _ref = Image.open(sys.argv[1])
# _offset = (int(sys.argv[2]), int(sys.argv[3]))

# For now, setting it to Link
_ref = Image.open("Link_Field.png")
_offset = (299,454)

username = sys.argv[1]
password = getpass.getpass(prompt="Enter password for {}:".format(username))

while True:
    try:
        s = requests.Session()
        s.mount('https://www.reddit.com', HTTPAdapter(max_retries=5))
        s.headers["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
        r = s.post("https://www.reddit.com/api/login/{}".format(username),
                   data={"user": username, "passwd": password, "api_type": "json"})
        s.headers['x-modhash'] = r.json()["json"]["data"]["modhash"]

        place_image(_ref, _offset)
    except:
        print("Got an error, restarting...")
    finally:
        time.sleep(random.randint(5,10))