import subprocess, os, platform, json, glob, hashlib, sys, traceback, urllib, urllib2
class MCNostalgia() :
    def __init__(self, path=None, execute=None, bindir=None, outputdir=None, origdir=None, patchdir="patches", cli=True) :
        if path == None :
            self.path = os.getcwd()
        else :
            self.path = path
        self.ostype = self.detectOS()
        if execute == None :
            if self.ostype == "windows" :
                self.execute = self.path+"\\bin\\bspatch.exe"
            else : self.execute = "bspatch"
        else : self.execute = execute
        if not os.path.exists(patchdir) :
            os.mkdir(patchdir)
        self.patchdir = os.path.abspath(patchdir)
        if bindir == None :
            self.bindir = self.getMCBinPath()
        else : self.bindir = bindir
        if outputdir == None :
            self.outputdir = self.bindir
        else :
            self.outputdir = outputdir
        if origdir == None :
            self.origdir = self.bindir
        self.order = []
        self.cli = cli
        self.status = []
        self.checknow = False
        self.done = (False, False)
    def writeStatus(self, value, statustype="StatusDisplay") :
        if self.cli :
            if statustype == "Progress" :
                self.dprogress = value
            elif statustype == "StatusDisplay" :
                self.status = value
                print value
        else :
            if statustype == "Progress" :
                self.dprogress = value
            elif statustype == "StatusDisplay" :
                self.status.append(value)
                self.checknow = True
            #print value
    def fetchAvailableVersions(self) :
        data = {"pversion":1, "list":True}
        urlobj = urllib2.urlopen("http://sonicrules.org/mcnweb.py", urllib.urlencode(data))
        urldata = urlobj.read()
        urlobj.close()
        jsondict = json.loads(urldata)
        uorder = jsondict[u"order"]
        for version in uorder :
            self.order.append(str(version))
        return self.order
    def fetchVersion(self, version) :
        if self.order == [] :
            self.fetchAvailableVersions()
        if version in self.order and version not in self.getDownloadedVersions() :
            data = {"pversion":1, "mcversion":version}
            urlobj = urllib2.urlopen("http://sonicrules.org/mcnweb.py", urllib.urlencode(data))
            urldata = urlobj.read()
            urlobj.close()
            jsondict = json.loads(urldata)
            url = str(jsondict[u"url"])
            os.mkdir(os.path.join(self.patchdir, version))
            for filename in ["minecraft.ptch", "lwjgl.ptch", "checksum.json", "lwjgl_util.ptch", "jinput.ptch"] :
                self.writeStatus("Downloading " + filename)
                urlobj = urllib2.urlopen(url + "/" + filename)
                data = urlobj.read()
                urlobj.close()
                x = open(os.path.join(self.patchdir, version, filename), "wb")
                x.write(data)
                x.close()
            del data
    def checkFiles(self) :
        try :
            jarfiles = [os.path.split(path)[1] for path in glob.glob(os.path.join(self.bindir, "*.jar"))]
            if "minecraft.jar" in jarfiles and "lwjgl.jar" in jarfiles and "lwjgl_util.jar" in jarfiles and "jinput.jar" in jarfiles :
                return True
            return False
        except :
            return False
    def patch(self, origfile, outputfile, patchfile) :
        try :
            subprocess.call([self.execute, origfile, outputfile, patchfile])
        except OSError :
            traceback.print_exc()
            if self.ostype == "linux" :
                self.writeStatus("You got an OSError. Since you are running Linux, you probably are missing the bspatch command. In most distributions, it is in the bsdiff package. Please install this and try again")
            elif self.ostype == "windows" :
                self.writeStatus("You got an OSError. Since you are running Windows, you are probably missing a file in your .minecraft bin folder. To fix this, delete your .minecraft bin folder, run the launcher, and login.")
            elif self.ostype == "mac" :
                self.writeStatus("You got an OSError. Since you are running a Mac, you are probably missing files in your minecraft bin folder. To fix this, delete your minecraft bin folder, run the launcher, and login.")
    def patchTo(self, mcversion) :
        if mcversion not in self.getDownloadedVersions() :
            self.fetchVersion(mcversion)
        hashinfo = self.getInfoForHashing(mcversion)
        for filename in ["minecraft", "lwjgl_util", "jinput", "lwjgl"] :
            self.patch(os.path.join(self.origdir, filename+".jar"), os.path.join(self.outputdir, filename+".jar"), os.path.join(self.patchdir, mcversion, filename+".ptch"))
            if self.checksum(mcversion, filename, hashinfo) :
                self.writeStatus("Patched" + " " + filename + ".jar successfully!")
            else :
                self.writeStatus(filename + ".jar was not patched successfully.")
                self.done = (True, False)
                return False
        self.done = (True, True)
        return True
    def checkOrig(self, version) :
        hashinfo = self.getInfoForHashing(version)
        for filename in ["minecraft", "lwjgl_util", "jinput", "lwjgl"] :
            fileobj = open(os.path.join(self.origdir, filename+".jar"), "rb")
            file_read = fileobj.read()
            fileobj.close()
            filemd5 = hashlib.md5(file_read).hexdigest()
            if filemd5 != hashinfo["CurrentVersion"][filename] :
                #print "Hashlib md5 = ", hashlib.md5(file_read).hexdigest()
                self.writeStatus("Checksum for %(filen)s does not match vanilla checksum for that file." % dict(filen=os.path.join(self.origdir, filename+".jar")))
                self.writeStatus("Your file's md5: " + filemd5)
                self.writeStatus("Vanilla file's md5: " + hashinfo["CurrentVersion"][filename])
                return False
        return True
    def checksum(self, mcversion, filename, hashinfo) :
        #print "Checksums not implemented yet!"
        fileobj = open(os.path.join(self.outputdir, filename+".jar"), "rb")
        file_read = fileobj.read()
        fileobj.close()
        if hashlib.md5(file_read).hexdigest() == hashinfo["OldVersion"][filename] :
            return True
        else : return False
    def getInfoForHashing(self, version) :
        fileobj = open(os.path.join(self.patchdir, version, "checksum.json"), "r")
        file_read = fileobj.read()
        fileobj.close()
        checksumdict = json.loads(file_read)
        oldversion = checksumdict[u"OldVersion"]
        currentversion = checksumdict[u"CurrentVersion"]
        return {"CurrentVersion":{"jinput":str(currentversion[u"jinput"]), "lwjgl_util":str(currentversion[u"lwjgl_util"]), "lwjgl":str(currentversion[u"lwjgl"]), "minecraft":str(currentversion[u"minecraft"]), "version":str(currentversion[u"version"])}, "OldVersion":{"jinput":str(oldversion[u"jinput"]), "lwjgl_util":str(oldversion[u"lwjgl_util"]), "lwjgl":str(oldversion[u"lwjgl"]), "minecraft":str(oldversion[u"minecraft"]), "version":str(oldversion[u"version"])}}
        
    def getDownloadedVersions(self) :
        versions = []
        for root, dirs, files in os.walk(os.path.join(self.path, self.patchdir)) :
            for directory in dirs :
                filename = glob.glob(os.path.join(self.path, self.patchdir, directory) + os.sep + "checksum.json")
                if len(filename) == 1 :
                    versions.append(directory)
        versions.sort()
        return versions
    
    def detectOS(self) :
        #plat = platform.uname()[0].lower()
        plat = sys.platform
        #if plat in ["linux", "unix", "solaris", "sunos"] :
        if plat.startswith("linux") :
            return "linux"
        elif plat == "darwin" :
            return "mac"
        elif plat.startswith("win") :
            return "windows"
        else :
            return "other"
    def normalize(self, path) :
        return os.path.normcase(path)
    def getMCBinPath(self) :
        ostype = self.detectOS()
        if ostype == "linux" :
            mcpath = os.path.expanduser('~/.minecraft/bin')
        elif ostype == "mac" :
            mcpath = os.path.expanduser('~/Library/Application Support/minecraft/bin')
        elif ostype == "windows" :
            if os.path.expandvars('$appdata') != None:
                mcpath = self.normalize(os.path.expandvars('$appdata/.minecraft/bin'))
            else:
                mcpath = self.normalize(os.path.expanduser('~/.minecraft/bin'))
        return mcpath 
