#!/usr/bin/python3

import argparse,re,os,requests,time
import urllib.parse,urllib.request,subprocess,sys,json,shutil,zipfile
from urllib.request import urlopen
from PIL import Image, ImageDraw, ImageFont
import functools

print = functools.partial(print, flush=True)
parser = argparse.ArgumentParser()

def split( o,s ):
    if s in o:
        return o.split(s)
    else:
        return [o]

def add_arguments(parser: argparse.ArgumentParser): 
    parser.add_argument("filename")


def run_command(command):
  try:
    time.sleep(0.1)
    apiRes= requests.get( command , stream=True )
    return apiRes.text.encode()
  except Exception as e:
    print("Error executing command:", str(e))
    return None


def main(args: argparse.Namespace):
    try:
      if args.filename[:4] == "http":
        with urllib.request.urlopen(urlstem+args.filename) as f:
          fObj= f.read(20000).decode()
        fLines= split(fObj,"\n")
      else:
        fObj= open(args.filename,'r')
        fLines= fObj.readlines()
        fObj.close()
    except:
        print( 'Failed to open file:', args.filename )
        exit(1)
    vDef= {"set":"","lay":"reset","art":None,"col":""}
    vPck= []
    for sLine in fLines:
        if sLine[0]==";":
            if sLine[1:4] not in vDef.keys():
              print("... WARNING: unrecognised setting\n",sLine)
            vDef[sLine[1:4]]= sLine[5:].strip()
        elif sLine[0]=="[":
            tVal= split( sLine[1:] , "]" )[0]
            vPck.append( {"name":tVal,"cards":[]} )
            for vKey in vDef.keys(): vPck[-1][vKey]= vDef[vKey]
        elif len( vPck ) >0:
            if sLine[:2]== "//":
                continue
            if re.search('\\s*(#)', sLine):
                pass
            elif re.search('^\\s*$', sLine):
                pass
            else:
                vQty= 1
                vSet= vDef["set"]
                v_cn= ""
                vNam= sLine.strip()
                if( vNam[0:3]=="(?)" ):
                  vSet= "?"
                  vNam= vNam[4:].strip()
                else:
                  tVal= re.search('(\\d+)\\s+(.*)', vNam)
                  if tVal:
                      vQty= int(tVal.groups()[0])
                      vNam= tVal.groups()[1]
                  tVal= re.search('(.*)\\((.+)\\)(.*)', vNam)
                  if tVal:
                      vSet= tVal.groups()[1]
                      v_cn= tVal.groups()[2].strip()
                      vNam= tVal.groups()[0].strip()
                vPck[-1]["cards"].append( {"q":vQty,"name":vNam,"set":vSet,"cn":v_cn} )
    for iPck in vPck:
        rCrd= []
        for i in range(len(iPck["cards"])):
            for j in range(iPck["cards"][i]["q"]):
                rCrd.append(i)
        if len(rCrd) != 20:
            print(f"WARNING: 20 cards expected, {len(rCrd)} found in {iPck['name'].replace(';',' variation ')}")
            if len(rCrd) <20:
                exit(1)
        (xS, yS)= (146 , 204) ; (xD,yD)= ( 92 , 128)
        (xN, yN)= (488 , 680) ; (x4,y4)= (244 , 340)
        (xL, yL)= (672 , 936) ; (x3,y3)= (336 , 468) ; y0= round( 6* xL * xL / yL -yL )
        imO = Image.new(mode = "RGB", size = (xL *6 , y0 +yL), color = (255, 255, 255))
        if True:
          xPos= [ 0,2,4,6,8,10 , 10,8,6,4,2,0 ] ; yPos= [ 0,0,0,0,0,0 , 2,2,2,2,2,2 ] ; scal= [ 0 for _ in range(12)] ; y0= round( 6* xL * xL/yL - yL ) ; dy= round(( y0 - 2* yL + yD *2)/6 )
          for i in range(12): xPos[i] *= x3 ; yPos[i] *= y3
          if iPck["lay"]== "reset":
            t= [iPck["cards"][rCrd[_]]["name"] == iPck["cards"][rCrd[_+1]]["name"] for _ in range(12,19)]
            if sum(t)==7: iPck["lay"]= "n8"
            elif sum(t)==6: iPck["lay"]= "n"+str( 17+ 9* t.index(False) )
            elif sum(t)==5:
              x= [ t.index(False) +1 , 0 ] ; x+= [ 7- t.index(False,sum(x)) ] ; x[1]= 8- sum(x) ; iPck["lay"]= "n"+"".join([str(_) for _ in x])
            elif sum(t)==4:
              x= [ t.index(False) +1 ] ; x += [ t.index(False,sum(x)) +1 -sum(x) ] ; x += [ t.index(False,sum(x)) +1 -sum(x) ] ; x += [ 8 -sum(x) ] ; iPck["lay"]= "n"+"".join([str(_) for _ in x])
            elif sum(t)==3:
              x= [ t.index(False) +1 ] ; x += [ t.index(False,sum(x)) +1 -sum(x) ] ; x += [ t.index(False,sum(x)) +1 -sum(x) ] ; x += [ t.index(False,sum(x)) +1 -sum(x) ] ; x += [ 8 -sum(x) ] ; iPck["lay"]= "n"+"".join([str(_) for _ in x])
            elif sum(t)==2:
              x= [ t.index(True) ] ; x += [ t.index(True,sum(x)+1) -2 -sum(x) ] ; x += [ 4 -sum(x) ] ; iPck["lay"]= "n"+"1"*x[0]+("3" if x[1]<0 else "2"+"1"*x[1]+"2")+"1"*x[2]
            elif sum(t)<2: iPck["lay"]= "n"+"".join(["12"[_] for _ in t])
            else: iPck["lay"]= "all"
          if "".join(sorted(list( iPck["lay"][1:] ))) in ["8","17","26","116","35","125","1115"]:
            if iPck["lay"][1:]== "8":
              (t,dx)= (0 , round((4* xL + 2* xD +x4 *2)/17) )
            elif "".join(sorted(list( iPck["lay"][1:] )))== "17":
              (t,dx)= (0 , round((3* xL + 2* xD +x4 *2)/14) )
            elif "".join(sorted(list( iPck["lay"][1:] ))) in ["26","35"]:
              (t,dx)= (0 , round((3* xL + 2* xD +x4 *2)/14) )
            elif "".join(sorted(list( iPck["lay"][1:] ))) in ["116","125"]:
              (t,dx)= (0 , round((2* xL + 2* xD +x4 *2)/11) )
            elif "".join(sorted(list( iPck["lay"][1:] )))== "1115":
              (t,dx)= (0 , round((xL + 2* xD +x4 *2)/8) )
            for i in iPck["lay"][1:]:
              if i< "5":
                xPos += [t +dx*3*_ for _ in [3,2,1,0][(4-int(i)):]] ; yPos += [ [yL*2] , [y0 -dy*3*_ for _ in [0,1,2,3][:int(i)]] ][i>"1"] ; scal += [0,0,0,0][(4-int(i)):] ; t += x3 *2 +dx*3 *(int(i)-1)
              else:
                xPos += [t +dx*3*_ for _ in [3,2,1,0][(4-int(i)):]] ; yPos += [y0 -dy*3*_ for _ in [0,1,2,3][:(int(i)-4)]] ; scal += [0,0,0,0][:(int(i)-4)] ; t += xD *2 +dx*(2 +3*(int(i)-5))
                xPos += [t +dx*2*_ for _ in [3,2,1,0]] ; yPos += [2*yL +dy*2*_ for _ in [3,2,1,0]] ; scal += [1,1,1,1] ; t += x4 *2 +dx*2 *3
          elif "".join(sorted(list( iPck["lay"][1:] ))) in ["44","134","224","1124","11114","233","1133","1223","11123","11222"]:
            if "".join(sorted(list( iPck["lay"][1:] ))) in ["11114","11123","11222"]:
              (t,dx)= (0 , round( xL /3 ))
            elif "".join(sorted(list( iPck["lay"][1:] ))) in ["2222","1223","1124","1133"]:
              (t,dx)= (0 , round( xL*2/4 ))
            elif "".join(sorted(list( iPck["lay"][1:] ))) in ["233","224","134"]:
              (t,dx)= (0 , round( xL*3/5 ))
            elif "".join(sorted(list( iPck["lay"][1:] )))== "44":
              (t,dx)= (0 , round( xL*4/6 ))
            for i in iPck["lay"][1:]:
                xPos += [t +dx*_ for _ in [3,2,1,0][(4-int(i)):]] ; yPos += [ [yL *2] , [y0 -dy*3*_ for _ in [0,1,2,3][:int(i)]] ][i >"1"] ; scal += [0,0,0,0][(4-int(i)):] ; t += x3 *2 +dx *(int(i)-1)
          elif "".join(sorted(list( iPck["lay"][1:] ))) in ["111122","111113","1111112"]:
            if "".join(sorted(list( iPck["lay"][1:] )))== "1111112":
              (t,dx,dy)= (0 , 3* xL - xN *4 , y0 +yD *2 )
              for i in range(1,8):
                if iPck["lay"][i]== "2":
                  xPos += [t +dx*_ for _ in [1,0]] ; yPos += [dy , yL *2] ; scal += [1,1] ; t += x4 *2 +dx
                else:
                  if i <4 or (i ==4 and "2" in iPck["lay"][:4]):
                    xPos += [t] ; yPos += [yL *2] ; scal += [0] ; t += x3 *2
                  else:
                    xPos += [t] ; yPos += [yL *2] ; scal += [1] ; t += x4 *2
            else:
              if "".join(sorted(list( iPck["lay"][1:] )))== "111113":
                (t,dx,dy)= (0 , xD , round( dy *3 ))
              elif "".join(sorted(list( iPck["lay"][1:] )))== "111122":
                (t,dx,dy)= (0 , 2* xD , dy *6 )
              for i in iPck["lay"][1:]:
                if i== "1":
                  xPos += [t] ; yPos += [y3*4] ; scal += [0] ; t += x3 *2
                else:
                  xPos += [t +dx*_ for _ in [3,2,1,0][(4-int(i)):]] ; yPos += [2*yL +dy*_ for _ in [3,2,1,0][(4-int(i)):]] ; scal += [1,1,1,1][(4-int(i)):] ; t += x4 *2 +dx *(int(i)-1)
          elif iPck["lay"] =="basic":
            (t,dx,dy)= (0 , round(( 6* xD -x4*4)/4 ) , round(( y0 +yD*2 - yL*2)/4 ))
            for i in range( 3 ):
                  xPos += [t] ; yPos += [y3*4] ; scal += [0] ; t += x3 *2
            xPos += [t +(x4*2 +dx)*_ for _ in [4,3,2,1,0]] ; yPos += [y3*4 +dy*_ for _ in [3,2,1,0,4]] ; scal += [1,1,1,1,1] ; t += x4 *10 +dx *4
          elif "mini2" in iPck["lay"] and "".join(sorted(list( iPck["lay"] ))[:-5]) =="112":
            (t,dx)= (0 , 6* xD -x4*2)
            for i in range( 1 , len( iPck["lay"] ) ):
              if iPck["lay"][i] =="1":
                  xPos += [t] ; yPos += [y3*4] ; scal += [0] ; t += x3 *2
              elif iPck["lay"][i] =="2":
                  xPos += [t+dx +_ for _ in [0,-x3]] ; yPos += [y0 , y0 -dy *3] ; scal += [0,0] ; t += x3 *2 +dx
              elif iPck["lay"][i:(i+4)] =="mini":
                  xPos += [t + x4*2*_ for _ in [0,1,2,3]] ; yPos += [y3*4 +dy*2*_ for _ in [0,1,2,3]] ; scal += [1,1,1,1] ; t += x4 *8
          else:
            (t,dx)= (3* xL - 4* xN , round((xD *12 - xN)/1) )
            for i in range(8):
              xPos += [t] ; yPos += [y3*4] ; scal += [1] ; t += x4 *2
            yPos= [round(( y0 -yL -yN)/2 ) +_ for _ in yPos]

        for i in range(19,-1,-1):
            tCrd= iPck["cards"][rCrd[i]]
            if tCrd["set"] == "link":
              vApi= tCrd["cn"]
            else:
              vApi= "https://api.scryfall.com/cards/"
              if tCrd["set"] == "?":
                vApi += "svow/3"
              elif tCrd["cn"] != "":
                vApi += tCrd["set"] +"/"+ tCrd["cn"]
              else:
                vApi += "named?set="+ tCrd["set"] +"&exact=" +'"'+ tCrd["name"] +'"'
            result= run_command( vApi )
            if result is None :
              time.sleep(0.1)
              result= run_command( vApi )
            if result is None :
              print(f"failed to contact scryfall: {tCrd['name']} ({tCrd['set']}) {tCrd['cn']}")
            else:
              jRes= json.loads(result)
              if jRes['object'] != 'card' :
                print(f"card not found for: {tCrd['name']} ({tCrd['set']}) {tCrd['cn']}")
                imD= Image.open(urlopen( "https://api.scryfall.com/cards/svow/3?format=image&version="+( ["large","normal","small"][scal[i]] )))
              else:
                if "image_uris" not in jRes.keys():
                  jRes["image_uris"]= jRes["card_faces"][0]["image_uris"]
                imD= Image.open(urlopen( jRes["image_uris"][["large","normal","small"][scal[i] %3]] ))
                if scal[i] == 3:
                  imD.thumbnail((xL/2,yL/2))
                elif scal[i] == 4:
                  imD.thumbnail((xN/2,yN/2))
                  # imD = imD.rotate(90, expand=True)
              imO.paste( imD ,( xPos[i] , yPos[i] ))
        try :
          imO.thumbnail((yL,xL))
          imO.save( iPck["set"] +" "+ " var ".join(split(iPck["name"],";")) +".png" )
          print( "Saved "+ iPck["set"] +" "+ " var ".join(split(iPck["name"],";")) +".png" , "("+iPck["lay"]+")" )
        except :
          print( '... WARNING: unable to save image' )

        # sCrd= run_command("https://api.scryfall.com/cards/search?q=unique:prints "
        

def entry():
    add_arguments(parser)
    args = parser.parse_args()
    main(args)

if __name__ == "__main__":
    entry()
