#!/usr/bin/env python
import os
import pickle
import sys
from argparse import ArgumentParser
from io import BytesIO

import unitypack
from unitypack.asset import Asset
from unitypack.export import OBJMesh
from unitypack.utils import extract_audioclip_samples


class UnityExtract:
	FORMAT_ARGS = {
		"audio": "AudioClip",
		"fonts": "Font",
		"images": "Texture2D",
		"models": "Mesh",
		"shaders": "Shader",
		"text": "TextAsset",
		"video": "MovieTexture",
	}

	def __init__(self, args):
		self.parse_args(args)

	def parse_args(self, args):
		p = ArgumentParser()
		p.add_argument("files", nargs="+")
		p.add_argument("--all", action="store_true", help="Extract all supported types")
		for arg, clsname in self.FORMAT_ARGS.items():
			p.add_argument("--" + arg, action="store_true", help="Extract %s" % (clsname))
		p.add_argument("-o", "--outdir", nargs="?", default="", help="Output directory")
		p.add_argument("--as-asset", action="store_true", help="Force open files as Asset format")
		p.add_argument("--filter", nargs="*", help="Filter extraction for a specific name")
		p.add_argument("-n", "--dry-run", action="store_true", help="Skip writing files")
		self.args = p.parse_args(args)

		self.handle_formats = []
		for a, classname in self.FORMAT_ARGS.items():
			if self.args.all or getattr(self.args, a):
				self.handle_formats.append(classname)

	def run(self):
		for file in self.args.files:
			if self.args.as_asset or file.endswith(".assets"):
				with open(file, "rb") as f:
					asset = Asset.from_file(f)
					self.handle_asset(asset)
				continue

			with open(file, "rb") as f:
				bundle = unitypack.load(f)

				for asset in bundle.assets:
					self.handle_asset(asset)

		return 0

	def get_output_path(self, filename):
		basedir = os.path.abspath(self.args.outdir)
		path = os.path.join(basedir, filename)
		dirs = os.path.dirname(path)
		if not os.path.exists(dirs):
			os.makedirs(dirs)
		return path

	def write_to_file(self, filename, contents, mode="w"):
		path = self.get_output_path(filename)

		if self.args.dry_run:
			print("Would write %i bytes to %r" % (len(contents), path))
			return

		with open(path, mode) as f:
			written = f.write(contents)

		print("Written %i bytes to %r" % (written, path))
		#exit()

	def handle_asset(self, asset):
		for id, obj in asset.objects.items():
			if obj.type not in self.handle_formats:
				continue

			def matches(name, filters):
				for f in filters:
					if f.lower() in name:
						return True
				return False

			d = obj.read()
			if self.args.filter and not matches(d.name.lower(), self.args.filter):
				continue

			if obj.type == "AudioClip":
				samples = extract_audioclip_samples(d)
				for filename, sample in samples.items():
					self.write_to_file(filename, sample, mode="wb")

			elif obj.type == "MovieTexture":
				filename = d.name + ".ogv"
				self.write_to_file(filename, d.movie_data, mode="wb")

			elif obj.type == "Shader":
				self.write_to_file(d.name + ".cg", d.script)

			elif obj.type == "Mesh":
				try:
					mesh_data = OBJMesh(d).export()
					self.write_to_file(d.name + ".obj", mesh_data, mode="w")
				except NotImplementedError as e:
					print("WARNING: Could not extract %r (%s)" % (d, e))
					mesh_data = pickle.dumps(d._obj)
					self.write_to_file(d.name + ".Mesh.pickle", mesh_data, mode="wb")

			elif obj.type == "Font":
				self.write_to_file(d.name + ".ttf", d.data, mode="wb")

			elif obj.type == "TextAsset":
				if isinstance(d.script, bytes):
					filename, mode = d.name + ".bin", "wb"
				else:
					filename, mode = d.name + ".txt", "w"
				self.write_to_file(filename, d.script, mode=mode)

			elif obj.type == "Texture2D":
				itr = 0
				filename = d.name +str(itr)+ ".png"
				print(str(asset.name) + str(asset.bundle) + str(asset.types))
				while os.path.isfile(self.get_output_path(filename)):
					itr += 1
					filename = d.name + str(itr) + ".png" 
				try:
					from PIL import ImageOps
				except ImportError:
					print("WARNING: Pillow not available. Skipping %r." % (filename))
					continue
				try:
					image = d.image
				except NotImplementedError:
					print("WARNING: Texture format not implemented. Skipping %r." % (filename))
					continue

				if image is None:
					print("WARNING: %s is an empty image" % (filename))
					continue

				print("Decoding %r" % (d))
				# Texture2D objects are flipped
				img = ImageOps.flip(image)
				# PIL has no method to write to a string :/
				output = BytesIO()
				img.save(output, format="png")
				self.write_to_file(filename, output.getvalue(), mode="wb")


def main():
	app = UnityExtract(sys.argv[1:])
	exit(app.run())


if __name__ == "__main__":
	main()