# THIS FILE IS A PART OF VCStudio
# PYTHON 3

import os
import datetime
import json

from studio import checklist
#import checklist

def get_legacy(project_location):
    
    # This function will read the .bos (Blender-Organizer Story) files. Primarily
    # to convert them to .vcss (VCStudio Story) files.
    # The concept is similar. But in order to work with them outside of reading
    # and writting the file directly. I want to make a dictionary format with in
    # the python. (Similarly to how I did with checklists and analytics)
    
    # In the .bos file there was what I thought was clever at the time but 
    # ultimatly a dumb idea to make EVENTS and not scenes. The idea was that in
    # a single event could be multiple scenes. But in reality it very rearly used
    # and creating scenes with in events created a potential user error situation.
    
    # So I want to get rid of all the EVENTS and read Scenes directly. Unless
    # the event is empty of scenes. In which case to create a scene with the
    # text of that event.
    
    data = {
        "fraction": 0.0,  # Percentage of the Scenes finished.
        "camera" : [0,0], # The position of where the user left
        "selected": [],   # List of selected items in the story editor
        "active": None,   # Active item.
        "scenes" : {},    # List of scenes.
        "arrows" : [],    # List of connections.       (A LIST. NOT DICT.)
        "links"  : [],    # List of links to files or assets.
        "markers": {},    # List of markers.
        "events" : {}  # List of frame like containers. (It will have similar)
        }              # function as events. But will have no text data with in
                       # it. It will be more like Blender's node editor's Frame.
    
    # Even tho I want to change radically the idea of events. We still need to
    # think in the events terms. Because we are parsing and old file. Funny how
    # I will need to write this thing twice. For both file-types.
    
    # I can't read it the way I read most other files. Because it's not line-
    # based. But more something like HTML file. So it's going to be a little 
    # issue.
    
    bos = open(project_location+"/pln/main.bos")
    bos = bos.read()
    
    cx, cy = 1, 1
    
    if "</camera>" in bos:
        camera = bos[bos.find("<camera>")+8:]
        camera = camera[:camera.find("</camera>")]
        camera = camera.split(",")
        
        for num, val in enumerate(camera):
            try:
                camera[num] = float(val)
            except:
                camera[num] = 0.0
        
        try:
            cx = float(camera[2])
            cy = float(camera[3])
        except:
            pass
        print (cy, cx)
        
        # Some stupid me made decision early in a story editor's life to use
        # per-pixel X coordinates and per line Y coordinates. Which is something
        # like 100 times the difference. Okay 50 ish is a default value for Y.
        
        # I'm not going to do anything about it right now. Maximum a bit later.
        
        camera = [camera[0], camera[1]] # I don't want scale to exist here. 
        
        data["camera"] = camera
        
    
    # Events. They are very important. Now in the Blender-Organizer I used an
    # HTML like format to mark shots and assets with in the text. But it's not
    # a very efficient way of doing it. Because it requiered constant parsing 
    # of the text in real time. Which caused quite a noticable lag. And also
    # every time I needed to read a part of it. I had to do parsing of the text.
    
    # So I guess the VCSS file format will be designed to deal with this kind of
    # thing. I'm actually more toward putting the text into separate files. Simple
    # text documents. And making the VCSS a linking system.    
    
    if "</event>" in bos:
        for event in bos.split("</event>")[:-1]:
            event = event[event.find("<event>")+8:]
            
            # Now we have text of the event. Let's parse out of it folowing 
            # stuff. We will need it if there are multiple scenes. Or when
            # there are no scenes at all.
            
            eventname     = event[event.find('"')+1:event.replace('"'," ",1).find('"')]
            
            # Let's parse the coordinates.
            
            c = event[event.find('[')+1:event.find(']')]
            c = c.split(",")
            
            eventpositon  = [float(c[0])*cx,float(c[2])*cy]
            eventsize     = [float(c[1])*cx,60.0]
            
            # Now since we know the name of the event and the sizes. We can
            # start parsing the scenes from the text with in the event.
            
            eventtext = event[event.find(']')+2:-1]
            
            # Now basically have to do the same exact thing with <scene> and 
            # later with <shot>, <item> to make all work.
            
            # But first let's record a scene if it has no scene in it.
            
            
            
            if not "<scene>" in eventtext:
                
                if eventname in data["scenes"]:
                    eventname = eventname + "_copy"
                
                data["scenes"][eventname] = {
                    "fraction":0.0,  # Percentage
                    "position":eventpositon,
                    "size":eventsize,
                    "parent":"",     # For when it's in a Frame (Event)
                    "shots":[[
                        "text_block",[["text", eventtext]]
                        ]]
                    }
            
            else:
                
                # If there are more then 1 scene per event. We want to create
                # an event, frame thing for them.
                
                aos = eventtext.count("<scene>")
                
                parent = "" #This will be it's name
                
                if aos > 1:
                    parent = eventname
                    
                    data["events"][eventname] = {
                        "position":eventpositon,
                        "size":[0,0]
                        }
                
                # Now let's continue parsing the scenes.
                
                for num, scene in enumerate(eventtext.split("</scene>")[:-1]):
                    
                    scenename = scene[scene.find('"')+1:scene.replace('"'," ",1).find('"')]
                    scenename = scenename.replace(" ", "_")
                    scenetext = scene[scene.replace('"', " ", 1).find('"')+1:-1]
                    
                    
                    scenesize = [eventsize[0] / aos , eventsize[1]]
                    sceneposition = [eventpositon[0] + scenesize[0]*num,
                                    eventpositon[1]]
                    
                    
                    data["scenes"][scenename] = {
                        "fraction":0.0,  # Percentage
                        "position":sceneposition,
                        "size":scenesize,
                        "parent":parent,     # For when it's in a Frame (Event)
                        "shots":[[
                            "text_block",[["text", scenetext]]
                            ]]
                        }
        
        # Parsing the text inside the scenes... OMG. Finding the SHOTS.
        
        for scenename in data["scenes"]:
            
            scenetext = data["scenes"][scenename]["shots"][0][1][0][1]
            
            # Okay so we have both scene name and scene text. So it's 
            # time so send this data into the main data thingy, right?
            # Wrong. Now we need to parse pointers to the shots, assets
            # and other stuff. This file format gonna take a while.
            
            # If you look into the history of this file on NotABug there
            # Will be a version with a novel-long article here in the 
            # comments. I was toying with ideas of how to make stuff 
            # work. I will not change the whole system a bit from what
            # I though there. (Using text pointers) and make it more like
            # list of shot_block or text_block. Which both are just text.
            # But one has a bit more metadata then the other. And inside 
            # which you can specify links, frases, images and simple text...
            
            # So first of all we need to break the text up into shots.
            
            shots = []
            
            sa = scenetext.count("<shot>") # Shots amount
            ts = scenetext         # Copy of the scene text to butcher
            
            for s in range(sa):
                
                # If the first part of the scene is not a shot.
                
                if not ts.startswith("<shot>"):
                    shots.append([
                        "text_block", [["text",ts[:ts.find("<shot>")]]]
                        ])
                
                # Now we need to erase the part from the ts.
                
                ts = ts[ts.find("<shot>")+6:]
                
                # Now we can parse the shot itself. We need the name.
                
                if ts.count('"') > 1:
                    shotname = ts[ts.find('"')+1:ts.replace('"', " ", 1).find('"')]
                    ts = ts[ts.replace('"', " ", 1).find('"')+1:]
                else:
                    shotname = "Unnamed"
                
                
                
                # Put it also into the list.
                
                shots.append([
                    "shot_block", shotname, [["text",ts[:ts.find("</shot>")]]]
                    ])
                
                # And erase it.
                
                ts = ts[ts.find("</shot>")+7:]
            
            shots.append([
                "text_block", [["text",ts]]
                ])
            
            
            
            # Now I want to get a fraction from a scene.
            
            shotsfractions = []
            
            for shot in shots:
                if shot[0] == "shot_block":
                    
                    # Let's see if it has a checklist.
                    
                    if os.path.exists(project_location\
                    +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress"):
                        
                        check = checklist.get_list(project_location\
                            +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress")
                        shotsfractions.append(check["fraction"])
                    else:
                        
                        folder = project_location\
                            +"/rnd/"+scenename+"/"+shot[1]
                        
                        try:
            
                            if len(os.listdir(folder+"/rendered")) > 0:
                                shotsfractions.append(1.0)
                            
                            elif len(os.listdir(folder+"/test_rnd")) > 0:
                                shotsfractions.append(0.8) 
                            
                            elif len(os.listdir(folder+"/opengl")) > 0:
                                shotsfractions.append(0.6) 
                                
                            elif len(os.listdir(folder+"/storyboard")) > 0:
                                shotsfractions.append(0.4)
                            
                            elif len(os.listdir(folder+"/extra")) > 0:
                                shotsfractions.append(0.2) 
                        except:
                            shotsfractions.append(0.0) 
            
            # Writting parsed verions of the scenes. With all the shots
            
            data["scenes"][scenename]["shots"] = shots
            
            # Now that we have SHOTS Fractions we need a scene fraction.
            
            try:
                data["scenes"][scenename]["fraction"] = \
                    sum(shotsfractions) / len(shotsfractions)
            except:
                data["scenes"][scenename]["fraction"] = 0.0
        
        # Now since we have shots and we have their factors. I'd like to do
        # parsing of the text parts inside shots and texts between shots to
        # find all the images, links and phrases.
        
        for scenename in data["scenes"]:
            for shotnum, shot in enumerate(data["scenes"][scenename]["shots"]):
                
                # Our shot data could be either text_block ot shot_block
                # and they are formatted a little bit differently.
                
                if shot[0] == "shot_block":
                    text = shot[2][0][1]
                else:
                    text = shot[1][0][1]
                
                # Now keep in mind that similar stuff has to be done again
                # when we saving the parsed text back
                
                textblock = []
                
                # This requires some extreme cleverness. Since I already hate
                # this file format. WHY IS IT SO COMPLICATED?! 
                
                part = ""
                skip = 0
                
                for num, letter in enumerate(text):
                    
                    if num >= skip:
                        
                        if part.endswith("<item>"):
                            
                            # putting the normal text in
                            if part[skip:].replace("<item>", ""):
                                textblock.append([
                                    "text", part[skip:].replace("<item>", "")
                                    ])
                            
                            # parsing the item
                            itempart = text[num:text[num:].find("</item>")+num]
                            link = itempart[itempart.find('"')+1:
                                            itempart.replace('"', " ", 1).find('"')]
                            
                            if link.startswith("/dev"):
                                link = link[4:]
                            
                            itemtext = itempart[itempart.replace('"', " ", 1).find('"')+1:]
                            
                            # puttin the link into the list
                            textblock.append([
                                "link", link, itemtext
                                ])
                            
                            # skiping to after the item
                            skip = text[num:].find("</item>")+6
                            part = ""
                        
                        # Images. With the <>
                        
                        elif part.endswith("<image>"):
                            
                            # putting the normal text in
                            if part[skip:].replace("<image>", ""):
                                textblock.append([
                                    "text", part[skip:].replace("<image>", "")
                                    ])
                            
                            # parsing the item
                            link = text[num:text[num:].find("</image>")+num]
                            
                            textblock.append([
                                "image", link
                                ])
                            
                            # skiping to after the item
                            skip = text[num:].find("</image>")+7
                            part = ""
                            
                        # Images. With the [] (a little older version but still
                        # should be supported by default)
                        
                        elif part.endswith("[image]"):
                            
                            # putting the normal text in
                            if part[skip:].replace("[image]", ""):
                                textblock.append([
                                    "text", part[skip:].replace("[image]", "")
                                    ])
                            
                            # parsing the item
                            link = text[num:text[num:].find("[/image]")+num]
                            
                            textblock.append([
                                "image", link
                                ])
                            
                            # skiping to after the item
                            skip = text[num:].find("[/image]")+7
                            part = ""
                        
                        # Now let's figure out the frases. Because it has a
                        # text based thing in the beginning. Or maybe it's going
                        # to be anything that was before it? Hmm...
                        elif part.endswith(" - ["):
                            
                            # putting the normal text in
                            if part[skip:].replace(" - [", ""):
                                textblock.append([
                                    "text", part[skip:].replace(" - [", "")
                                    ])
                            
                            # I designed the - [ thing to type conversation
                            # easier. Example:
                            
                            # John - [Hello, World.]
                            
                            # This would be John saying the words "Hello, World."
                            # but with the inclusion of <item> it became quite a
                            # problem. Because I would like John to be a link.
                            
                            # It ended up looking something like:
                            
                            #<itme>"/dev/chr/John"John</item> - [Hello, World.]
                            
                            # This is a bit of a problem. Because any link that
                            # marked as <item> is already in a textblock list.
                            
                            character = []
                            
                            # If it's a link
                            
                            if textblock[-1][0] == "link":
                                character = textblock[-1]
                                del textblock[-1]
                            
                            # If it's just a text
                                
                            elif textblock[-1][0] == "text":
                                character = textblock[-1][1]
                                character = character[character.rfind("\n")+1:]
                                
                                if character:
                                    textblock[-1][1] = textblock[-1][1].replace(character, "")
                                    character = ["text", character]
                            
                            # Now let's get the frase
                            frase = text[num:text[num:].find("]")+num]
                            
                            # If any character. Put it into a textblock
                                
                            if character:
                                textblock.append([
                                "frase", character, frase
                                ])
                                
                            
                            # skiping to after the item
                            skip = text[num:].find("]")
                            part = ""
                                    
                        else:
                            part = part + letter 
                
                # LAST PART (WHY DIDN'T I THINK ABOUT ERLIER OMG)
                textblock.append([
                    "text", part[skip:]
                    ])                
                
                if shot[0] == "shot_block":
                    data["scenes"][scenename]["shots"][shotnum][2] = textblock
                else:
                    data["scenes"][scenename]["shots"][shotnum][1] = textblock   
                
    # Okay this was event. LOL. This is a large function to parse them all. 
    # Crazy complex bastards. Anyway. It's over. And we have to only do the 
    # easy rest of it. Untill we will get to calculating the project. Because
    # we need to filter out all scenes that are not in the main chain.
    
    # Let's start with hard thing first. So to feel good when having to do the
    # easy stuff in the end. ARROWS. (connections between scenes)
    
    
        
    # To make it work. We need to fisrt of all earase all the stuff before
    # the first arrow. Because <arrow> could be mentioned in the script it
    # self. Similarly <image> is too. So it's good to complitelly clear bos
    # out of anything that came before.
    
    bos = bos[bos.rfind("</event>"):]
    
    # From here everything is PER LINE BASED... YEAH!!!!
    
    bos = bos.split("\n")
    
    for line in bos:
        
        # This is arrows...
        
        if line.startswith("<arrow>"):
            
            arrow = line[line.find("<")+7:line.rfind("</")].split(" --> ")
            newarrow = []
            for i in arrow:
                i = i.split(",")
                if i[0] != "-1":
                    newarrow.append([
                        "scene", i[1][1:-1]
                        ])
                else:
                    newarrow.append(
                        i[1][1:-1]
                        )
            data["arrows"].append(newarrow)
        
        # And this is links. Formerly known as Images. Initially the idea was to
        # just store images. But later. I started adding assets like this as well.
        
        elif line.startswith("<image>"):        
            stuff = line.split(",")
            link = stuff[3][1:-1]
            coordinates = []
            try:
                coordinates.append(float(stuff[0].replace("<image>", ""))*cx)
            except:
                coordinates.append(0.0)
            
            try:
                coordinates.append(float(stuff[1])*cy)
            except:
                coordinates.append(0.0)
            
            # Lately the primary reason to use <image> was linking assets directly
            # into story editor's space. But it was a hack. Basically I was
            # linking a preview image. Preview.png or Preview.jpg from the 
            # renders of the asset. And the drawer of the images in the story-
            # editor already knew to redirect the clicks to a different function.
            # But for VCStudio I want to link to asset or a file.
            
            linktype = "file"
            
            if "/renders/Preview." in link:
                linktype = "asset"
                link = link[:link.rfind("/renders/Preview.")].replace("/dev", "")
            
            data["links"].append([
                linktype, link, coordinates, ""
                ])
        
        # Markers. I nearly forgot about the markers. In the Story-Editor of
        # Blender-Orgaznier they were something like markers in VSE in Blender.
        # Vertical lines visible on every altitude. 
        
        elif line.startswith("<marker>"):
            marker = line.replace("<marker>", "").replace("</marker>", "").split(",")
            
            try:
                markloc = float(marker[0])
            except:
                markloc = 0.0
            
            markstring = marker[1].replace('"', "")
            
            # I do want to do something quite interesting with markers in the
            # VCStudio tho. I want them to be like little nodes with text while 
            # in the frame. And be like directional guides. Sticking to the
            # edges of the screen when outside the screen. Something like items
            # that are outside of the map in the videogames. So you can see
            # which direction are they. For this markers will need to have
            # both X and Y coordinates.
            
            data["markers"][markstring] = [markloc, 0.0, ""]
            
            # For now the Y is 0. Because we are reading from the old version.
            
        
    # Okay we've got the Arrows data. Pretty much all the data. Now we just 
    # need to calculate the scenes fraction. For that we need to recreate the
    # train of scenes. From START till END.
    
    fractions = []
    lastarrow = 'start'
    
    
    for i in data["arrows"]:
        if lastarrow == "end":
            break
        for arrow in data["arrows"]:
            if arrow[0] == lastarrow:
                lastarrow = arrow[1]
                if arrow[1] != "end":
                    fractions.append(
                        data["scenes"][arrow[1][1]]["fraction"]
                        )
                else:
                    break
    
    # FINAL STUFF...
    
    try:
        data["fraction"] = sum(fractions) / len(fractions)
    except:
        data["fraction"] = 0.0
    
    #save(project_location, data)
    #data = load(project_location)
    
    return data
    
def get_asset_data(win, name, force=False):
    
    # This function will return a data of about the asset.
    
    if name not in win.assets or force:
        
        data = {
            "fraction":0.0
            }
        
        try:    
            # Now let's get a fraction of the asset
            if os.path.exists(win.project+"/ast/"+name+".blend"):
                data["fraction"] = 1.0
            else:
                check = checklist.get_list(win.project+"/dev/"+name+"/asset.progress")
                data["fraction"] = check["fraction"]
        except:
            pass
        
        win.assets[name] = data
    
    return win.assets[name]
    
def save(project, story):
    
    # This is a save of the new VCStudio. Which is using a standard file format.
    # so people could parse the data themselves. ( Or because I'm lazy )
    
    try:
        os.mkdir(project+'/pln/')
    except:
        pass
    
    with open(project+'/pln/story.vcss', 'w') as fp:
        json.dump(story, fp, sort_keys=True, indent=4)

def load(project):
    
    # This is a convertion back from JSON data to out beloved python dict
    try:
        with open(project+'/pln/story.vcss') as json_file: 
            data = json.load(json_file) 
    except:
        
        # If there is no file.
        data = {
        "fraction": 0.0,  # Percentage of the Scenes finished.
        "camera" : [0,0], # The position of where the user left
        "selected": [],   # List of selected items in the story editor
        "active": None,   # Active item.
        "scenes" : {},    # List of scenes.
        "arrows" : [],    # List of connections.       (A LIST. NOT DICT.)
        "links"  : [],    # List of links to files or assets.
        "markers": {},    # List of markers.
        "events" : {}  # List of frame like containers. (It will have similar)
        }              # function as events. But will have no text data with in
                       # it. It will be more like Blender's node editor's Frame.
    
    
    # I forgot to update the scenes analytics lol.
    
    for scenename in data["scenes"]:
        
        shotsfractions = []
        for shot in data["scenes"][scenename]["shots"]:
            if shot[0] == "shot_block":
                
                # Let's see if it has a checklist.
                
                if os.path.exists(project\
                +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress"):
                    
                    check = checklist.get_list(project\
                        +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress")
                    shotsfractions.append(check["fraction"])
                else:
                    
                    folder = project\
                        +"/rnd/"+scenename+"/"+shot[1]
                    
                    try:
        
                        if len(os.listdir(folder+"/rendered")) > 0:
                            shotsfractions.append(1.0)
                        
                        elif len(os.listdir(folder+"/test_rnd")) > 0:
                            shotsfractions.append(0.8) 
                        
                        elif len(os.listdir(folder+"/opengl")) > 0:
                            shotsfractions.append(0.6) 
                            
                        elif len(os.listdir(folder+"/storyboard")) > 0:
                            shotsfractions.append(0.4)
                        
                        elif len(os.listdir(folder+"/extra")) > 0:
                            shotsfractions.append(0.2) 
                    except:
                        shotsfractions.append(0.0) 
        
        try:
            data["scenes"][scenename]["fraction"] = \
                sum(shotsfractions) / len(shotsfractions)
        except:
            data["scenes"][scenename]["fraction"] = 0.0
        
        
        
        
    fractions = []
    lastarrow = 'start'
    
    
    for i in data["arrows"]:
        if lastarrow == "end":
            break
        for arrow in data["arrows"]:
            if arrow[0] == lastarrow:
                lastarrow = arrow[1]
                if arrow[1] != "end":
                    fractions.append(
                        data["scenes"][arrow[1][1]]["fraction"]
                        )
                else:
                    break
    
    # FINAL STUFF...
    
    try:
        data["fraction"] = sum(fractions) / len(fractions)
    except:
        data["fraction"] = 0.0    
    
            
    return data
    
def undo_record(win):
    
    # This function will record undo of the story. The undo will be basically 
    # copies of the script JSON file as a whole. The /pln/story.vcss
    
    # This function will not read the files. It's not nessesary. But it will
    # make a copy of the whole thing. So I guess we need to implement some kind
    # of limiter. 
    
    limit = int(win.settings["Undo_Limit"])
    
    # Now let's advance the undo history 1 forward.
    win.undo_index = min(len(win.undo_history)+1, limit)
    
    # Let's read our file
    f = open(win.project+'/pln/story.vcss')
    
    win.undo_history.append(f.read())
    
    # And now let's limit our selves
    
    win.undo_history = win.undo_history[0-limit:]
    

# Following 2 functions are a big buggy at the moment. I will aprecieate if
# someone going to look at them. Maybe it's to do with studio/studio_storyLayer.py
# where I was testing them. But they work. Just not every time.

def undo(win):

    # This function will do the undo. Tho this function will not nessesarily
    # sens the Ctrl - Z by it self. It should be activated from any place
    # in the UI.
    
    # Let's move our index back 1 way.
    win.undo_index = max(0, win.undo_index-1)
    
    
    # Now let's read our data
    d = win.undo_history[win.undo_index]
    
    # And let's save the data back to the file
    f = open(win.project+'/pln/story.vcss', "w")
    f.write(d)
    f.close()
    
    # And let's refresh story
    win.story = load(win.project)

def redo(win):

    # This function will do the redo. Tho this function will not nessesarily
    # sens the Ctrl - Y by it self. It should be activated from any place
    # in the UI.
    
    # Let's move our index back 1 way.
    win.undo_index = min(len(win.undo_history)-1, win.undo_index+1)
    
    
    # Now let's read our data
    d = win.undo_history[win.undo_index]
    
    # And let's save the data back to the file
    f = open(win.project+'/pln/story.vcss', "w")
    f.write(d)
    f.close()
    
    # And let's refresh story
    win.story = load(win.project)