import os
from optparse import make_option

from django.core.management.base import BaseCommand
from django.template import Template

from symmetric.management.generateconnections import GenerateConnectionsCommand

READ_OBJECT_METHOD = """
	public void read{{ name }}{% if param %}With{{ param }}({{ param_type }} {{ name_lower }}{{ param }}){% else %}(){% endif %}
	{
		String path = {% if param %}String.format(URL_{{ name|upper }}_WITH_{{ param|upper }}, {{ name_lower }}{{ param }}){% else %}URL_{{ name|upper }}{% endif %};
		performRequestWithObject(null, API.ACTION_READ, REQUEST_READ_{{ name|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, false);
	}

	public static void read{{ name }}{% if param %}With{{ param }}({{ param_type }} {{ name_lower }}{{ param }}, {% else %}({% endif %}RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.read{{ name }}{% if param %}With{{ param }}({{ name_lower }}{{ param }}){% else %}(){% endif %};
	}"""

READ_LIST_METHOD = """
	public void read{{ name_plural }}()
	{
		performRequestWithObject(null, API.ACTION_LIST, REQUEST_READ_{{ name_plural|upper }}, URL_{{ name_plural|upper }}, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, false);
	}

	public void read{{ name_plural }}(APIRequestParams params)
	{
		this.requestParams = params;
		read{{ name_plural }}();
	}

	public static void read{{ name_plural }}(APIRequestParams params, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.requestParams = params;
		connection.read{{ name_plural }}();
	}"""

CREATE_METHOD = """
	public void create{{ name }}({{ name }} {{ name_lower }})
	{
		performRequestWithObject({{ name_lower }}, API.ACTION_CREATE, REQUEST_CREATE_{{ name|upper }}, URL_{{ name_plural|upper }}, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, {% if hmac %}true{% else %}false{% endif %});
	}

	public static void create{{ name }}({{ name }} {{ name_lower }}, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.create{{ name }}({{ name_lower }});
	}"""

UPDATE_METHOD = """
	public void update{{ name }}({{ name }} {{ name_lower }})
	{
		String path = {% if param %}String.format(URL_{{ name|upper }}, {{ name_lower }}.getObjectId()){% else %}URL_{{ name|upper }}{% endif %};
		performRequestWithObject({{ name_lower }}, API.ACTION_UPDATE, REQUEST_UPDATE_{{ name|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, {% if hmac %}true{% else %}false{% endif %});
	}

	public static void update{{ name }}({{ name }} {{ name_lower }}, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.update{{ name }}({{ name_lower }});
	}"""

DELETE_METHOD = """
	public void delete{{ name }}({{ name }} {{ name_lower }})
	{
		performRequestWithObject(null, API.ACTION_DELETE, REQUEST_DELETE_{{ name|upper }}, String.format(URL_{{ name|upper }}, {{ name_lower }}.getObjectId()), {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, false);
	}

	public static void delete{{ name }}({{ name }} {{ name_lower }}, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.delete{{ name }}({{ name_lower }});
	}"""

READ_RELATED_LIST_METHOD = """
	public void read{{ name_plural }}For{{ parent_name }}{% if param %}With{{ param }}({{ param_type }} {{ parent_name_lower }}{{ param }}){% else %}(){% endif %}
	{
		String path = {% if param %}String.format(URL_{{ parent_name|upper }}_{{ name_plural|upper }}_WITH_{{ param|upper }}, {{ parent_name_lower }}{{ param }}){% else %}URL_{{ parent_name|upper }}_{{ name_plural|upper }}{% endif %};
		performRequestWithObject(null, API.ACTION_LIST, REQUEST_READ_{{ parent_name|upper }}_{{ name_plural|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, false);
	}

	public void read{{ name_plural }}For{{ parent_name }}{% if param %}With{{ param }}({{ param_type }} {{ parent_name_lower }}{{ param }}, {% else %}({% endif %}APIRequestParams params)
	{
		this.requestParams = params;
		read{{ name_plural }}For{{ parent_name }}{% if param %}With{{ param }}({{ parent_name_lower }}{{ param }}){% else %}(){% endif %};
	}

	public static void read{{ name_plural }}For{{ parent_name }}{% if param %}With{{ param }}({{ param_type }} {{ parent_name_lower }}{{ param }}, {% else %}({% endif %}APIRequestParams params, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.requestParams = params;
		connection.read{{ name_plural }}For{{ parent_name }}{% if param %}With{{ param }}({{ parent_name_lower }}{{ param }}){% else %}(){% endif %};
	}"""

CREATE_RELATED_METHOD = """
	public void create{{ name }}For{{ parent_name }}{% if param %}With{{ param }}{% endif %}({{ name }} {{ name_lower }}{% if param %}, {{ param_type }} {{ parent_name_lower }}{{ param }}{% endif %})
	{
		String path = {% if param %}String.format(URL_{{ parent_name|upper }}_{{ name_plural|upper }}_WITH_{{ param|upper }}, {{ parent_name_lower }}{{ param }}){% else %}URL_{{ parent_name|upper }}_{{ name_plural|upper }}{% endif %};
		performRequestWithObject({{ name_lower }}, API.ACTION_CREATE, REQUEST_CREATE_{{ parent_name|upper }}_{{ name|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, {% if hmac %}true{% else %}false{% endif %});
	}

	public static void create{{ name }}For{{ parent_name }}{% if param %}With{{ param }}{% endif %}({{ name }} {{ name_lower }}{% if param %}, {{ param_type }} {{ parent_name_lower }}{{ param }}{% endif %}, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.create{{ name }}For{{ parent_name }}{% if param %}With{{ param }}({{ name_lower }}, {{ parent_name_lower }}{{ param }}){% else %}({{ name_lower }}){% endif %};
	}"""

READ_RELATED_OBJECT_METHOD = """
	public void read{{ name }}For{{ parent_name }}{% if param %}With{{ param }}({{ param_type }} {{ parent_name_lower }}{{ param }}){% else %}(){% endif %}
	{
		String path = {% if param %}String.format(URL_{{ parent_name|upper }}_{{ name|upper }}_WITH_{{ param|upper }}, {{ parent_name_lower }}{{ param }}){% else %}URL_{{ parent_name|upper }}_{{ name|upper }}{% endif %};
		performRequestWithObject(null, API.ACTION_READ, REQUEST_READ_{{ parent_name|upper }}_{{ name|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, false);
	}

	public void read{{ name }}For{{ parent_name }}{% if param %}With{{ param }}({{ param_type }} {{ parent_name_lower }}{{ param }}, RequestListener listener){% else %}(RequestListener listener){% endif %}
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.read{{ name }}For{{ parent_name }}{% if param %}With{{ param }}({{ parent_name_lower }}{{ param }}){% else %}(){% endif %});
	}"""

UPDATE_RELATED_METHOD = """
	public void update{{ name }}For{{ parent_name }}{% if param %}With{{ param }}{% endif %}({{ name }} {{ name_lower }}{% if param %}, ({{ param_type }} {{ parent_name_lower }}{{ param }}{% endif %})
	{
		String path = {% if param %}String.format(URL_{{ parent_name|upper }}_{{ name|upper }}_WITH_{{ param|upper }}, {{ parent_name_lower }}{{ param }}){% else %}URL_{{ parent_name|upper }}_{{ name|upper }}{% endif %};
		performRequestWithObject({{ name_lower }}, API.ACTION_UPDATE, REQUEST_UPDATE_{{ parent_name|upper }}_{{ name|upper }}, path, {% if https %}true{% else %}false{% endif %}, {% if login %}true{% else %}false{% endif %}, {% if hmac %}true{% else %}false{% endif %});
	}

	public static void update{{ name }}For{{ parent_name }}{% if param %}With{{ param }}{% endif %}({{ name }} {{ name_lower }}{% if param %}, ({{ param_type }} {{ parent_name_lower }}{{ param }}{% endif %}, RequestListener listener)
	{
		{{ app_name }}Connection connection = new {{ app_name }}Connection(listener);
		connection.update{{ name }}For{{ parent_name }}{% if param %}With{{ param }}({{ name_lower }}, {{ parent_name_lower }}{{ param }}){% else %}({{ name_lower }}){% endif %};
	}"""

def indent(s, indentation):
	return '\n'.join([indentation + line for line in s.splitlines()])

READ_OBJECT_RESPONSE = indent("""
case REQUEST_READ_{{ name|upper }}:
	responseObject = new {{ name }}(new JSONObject(response));
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidRead{{ name }}({{ app_name }}Connection.this, ({{ name }})responseObject); } });
	break;""", '\t' * 7)

READ_LIST_RESPONSE = indent("""
case REQUEST_READ_{{ name_plural|upper }}:
	jsonArray = new JSONArray(response);
	responseArray = new {{ name }}[jsonArray.length()];
	for(int i = 0; i < jsonArray.length(); ++i) {% templatetag openbrace %}{% if subclasses %}
		JSONObject jsonObject = jsonArray.getJSONObject(i);{% for subclass in subclasses %}
		{% if forloop.counter0 %}else {% endif %}if(jsonObject.has("{{ subclass.0 }}"))
			responseArray[i] = new {{ subclass.1 }}(jsonObject);{% endfor %}{% else %}
		responseArray[i] = new {{ name }}(jsonArray.getJSONObject(i));{% endif %}
	}
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidRead{{ name_plural }}({{ app_name }}Connection.this, ({{ name }}[])responseArray); } });
	break;""", '\t' * 7)

CREATE_RESPONSE = indent("""
case REQUEST_CREATE_{{ name|upper }}:
	(({{ name }}){{ app_name }}Connection.this.requestObject).setObjectId(API.parseInt({{ app_name }}Connection.this.connection.getHeaderField(XHEADER_NEW_OBJECT_ID)));
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidCreate{{ name }}({{ app_name }}Connection.this, ({{ name }}){{ app_name }}Connection.this.requestObject); } });
	break;""", '\t' * 7)

UPDATE_RESPONSE = indent("""
case REQUEST_UPDATE_{{ name|upper }}:
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidUpdate{{ name }}({{ app_name }}Connection.this, ({{ name }}){{ app_name }}Connection.this.requestObject); } });
	break;""", '\t' * 7)

DELETE_RESPONSE = indent("""
case REQUEST_DELETE_{{ name|upper }}:
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidDelete{{ name }}({{ app_name }}Connection.this, ({{ name }}){{ app_name }}Connection.this.requestObject); } });
	break;""", '\t' * 7)

READ_RELATED_LIST_RESPONSE = indent("""
case REQUEST_READ_{{ parent_name|upper }}_{{ name_plural|upper }}:
	jsonArray = new JSONArray(response);
	responseArray = new {{ name }}[jsonArray.length()];
	for(int i = 0; i < jsonArray.length(); ++i) {% templatetag openbrace %}{% if subclasses %}
		JSONObject jsonObject = jsonArray.getJSONObject(i);{% for subclass in subclasses %}
		{% if forloop.counter0 %}else {% endif %}if(jsonObject.has("{{ subclass.0 }}"))
			responseArray[i] = new {{ subclass.1 }}(jsonObject);{% endfor %}{% else %}
		responseArray[i] = new {{ name }}(jsonArray.getJSONObject(i));{% endif %}
	}
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidRead{{ name_plural }}For{{ parent_name }}({{ app_name }}Connection.this, ({{ name }}[])responseArray); } });
	break;""", '\t' * 7)

CREATE_RELATED_RESPONSE = indent("""
case REQUEST_CREATE_{{ parent_name|upper }}_{{ name|upper }}:
	(({{ name }}){{ app_name }}Connection.this.requestObject).setObjectId(API.parseInt({{ app_name }}Connection.this.connection.getHeaderField(XHEADER_NEW_OBJECT_ID)));
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidCreate{{ name }}For{{ parent_name }}({{ app_name }}Connection.this, ({{ name }}){{ app_name }}Connection.this.requestObject); } });
	break;""", '\t' * 7)

READ_RELATED_OBJECT_RESPONSE = indent("""
case REQUEST_READ_{{ parent_name|upper }}_{{ name|upper }}:
	responseObject = new {{ name }}(new JSONObject(response));
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidRead{{ name }}For{{ parent_name }}({{ app_name }}Connection.this, ({{ name }})responseObject); } });
	break;""", '\t' * 7)

UPDATE_RELATED_RESPONSE = indent("""
case REQUEST_UPDATE_{{ parent_name|upper }}_{{ name|upper }}:
	if({{ app_name }}Connection.this.listener != null)
		API.runOnUiThread(new Runnable() { public void run() { {{ app_name }}Connection.this.listener.{{ app_name_lower }}ConnectionDidUpdate{{ name }}For{{ parent_name }}({{ app_name }}Connection.this, ({{ name }}){{ app_name }}Connection.this.requestObject); } });
	break;""", '\t' * 7)

class Command(BaseCommand, GenerateConnectionsCommand):
	help = 'Generate Android connections for API endpoints.'
	option_list = BaseCommand.option_list + GenerateConnectionsCommand.option_list + (
		make_option('--connection',
			type='string',
			dest='connection',
			default='APIURLConnection',
			help='Connection class to use that should have the same interface as ???.'),
		make_option('--package',
			action='store',
			dest='package',
			type='string',
			default='com.business.app',
			help='Set the Android package name.'),
	)

	def extra_context(self):
		return {'package': self.package, 'connection': self.connection}

	def handle(self, *args, **options):
		# Remember options
		self.package = options['package']
		self.connection = options['connection']

		# Create the templates
		self.templates = []
		template_path = os.path.join(os.path.normpath(os.path.dirname(__file__) + '/../templates'), 'android-connection.java')
		with open(template_path) as f:
			self.templates.append(Template(f.read()))
		self.template_extensions = ['java']

		# Create the mappings
		request_types_base = 'REQUEST_%s_%s'
		request_types_mapping = {
			'READ_OBJECT': Template(request_types_base % ('READ', '{{ name|upper }}')),
			'READ_LIST': Template(request_types_base % ('READ', '{{ name_plural|upper }}')),
			'CREATE': Template(request_types_base % ('CREATE', '{{ name|upper }}')),
			'UPDATE': Template(request_types_base % ('UPDATE', '{{ name|upper }}')),
			'DELETE': Template(request_types_base % ('DELETE', '{{ name|upper }}')),
			'READ_RELATED_LIST': Template(request_types_base % ('READ', '{{ parent_name|upper }}_{{ name_plural|upper }}')),
			'CREATE_RELATED': Template(request_types_base % ('CREATE', '{{ parent_name|upper }}_{{ name|upper }}')),
			'READ_RELATED_OBJECT': Template(request_types_base % ('READ', '{{ parent_name|upper }}_{{ name|upper }}')),
			'UPDATE_RELATED': Template(request_types_base % ('UPDATE', '{{ parent_name|upper }}_{{ name|upper }}')),
			'RemoveDuplicates': True,
			'Sort': True
		}

		urls_mapping = {}
		urls_mapping['READ_OBJECT'] = Template('public static final String URL_{{ name|upper }}{% if param %}_WITH_{{ param|upper }}{% endif %} = "{{ url_format }}";')
		urls_mapping['READ_LIST'] = urls_mapping['CREATE'] = Template('public static final String URL_{{ name_plural|upper }} = "{{ url_format }}";')
		urls_mapping['UPDATE'] = urls_mapping['DELETE'] = Template('public static final String URL_{{ name|upper }} = "{{ url_format }}";')
		urls_mapping['READ_RELATED_LIST'] = urls_mapping['CREATE_RELATED'] = Template('public static final String URL_{{ parent_name|upper }}_{{ name_plural|upper }}{% if param %}_WITH_{{ param|upper }}{% endif %} = "{{ url_format }}";')
		urls_mapping['READ_RELATED_OBJECT'] = urls_mapping['UPDATE_RELATED'] = Template('public static final String URL_{{ parent_name|upper }}_{{ name|upper }}{% if param %}_WITH_{{ param|upper }}{% endif %}  = "{{ url_format }}";')
		urls_mapping['RemoveDuplicates'] = True

		listener_method_decls_base = 'public void {{ app_name_lower }}ConnectionDid%s({{ app_name }}Connection connection, %s);'
		listener_method_decls_mapping = {
			'READ_OBJECT': Template(listener_method_decls_base % ('Read{{ name }}', '{{ name }} {{ name_lower }}')),
			'READ_LIST': Template(listener_method_decls_base % ('Read{{ name_plural }}', '{{ name }}[] {{ name_plural_lower }}')),
			'CREATE': Template(listener_method_decls_base % ('Create{{ name }}', '{{ name }} {{ name_lower }}')),
			'UPDATE': Template(listener_method_decls_base % ('Update{{ name }}', '{{ name }} {{ name_lower }}')),
			'DELETE': Template(listener_method_decls_base % ('Delete{{ name }}', '{{ name }} {{ name_lower }}')),
			'READ_RELATED_LIST': Template(listener_method_decls_base % ('Read{{ name_plural }}For{{ parent_name }}', '{{ name }}[] {{ name_plural_lower }}')),
			'CREATE_RELATED': Template(listener_method_decls_base % ('Create{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}')),
			'READ_RELATED_OBJECT': Template(listener_method_decls_base % ('Read{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}')),
			'UPDATE_RELATED': Template(listener_method_decls_base % ('Update{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}')),
			'RemoveDuplicates': True,
			'Sort': True
		}

		basic_listener_methods_base = 'public void {{ app_name_lower }}ConnectionDid%s({{ app_name }}Connection connection, %s) { Log.i(API.TAG, "{{ app_name }} did %s."); }'
		basic_listener_methods_mapping = {
			'READ_OBJECT': Template(basic_listener_methods_base % ('Read{{ name }}', '{{ name }} {{ name_lower }}', 'read {{ name_lower }}')),
			'READ_LIST': Template(basic_listener_methods_base % ('Read{{ name_plural }}', '{{ name }}[] {{ name_plural_lower }}', 'read " + Integer.toString({{ name_plural_lower }}.length) + " {{ name_plural_lower }}')),
			'CREATE': Template(basic_listener_methods_base % ('Create{{ name }}', '{{ name }} {{ name_lower }}', 'create {{ name_lower }}')),
			'UPDATE': Template(basic_listener_methods_base % ('Update{{ name }}', '{{ name }} {{ name_lower }}', 'update {{ name_lower }}')),
			'DELETE': Template(basic_listener_methods_base % ('Delete{{ name }}', '{{ name }} {{ name_lower }}', 'delete {{ name_lower }}')),
			'READ_RELATED_LIST': Template(basic_listener_methods_base % ('Read{{ name_plural }}For{{ parent_name }}', '{{ name }}[] {{ name_plural_lower }}', 'read " + Integer.toString({{ name_plural_lower }}.length) + " {{ name_plural_lower }} for {{ parent_name_lower }}')),
			'CREATE_RELATED': Template(basic_listener_methods_base % ('Create{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}', 'create {{ name_lower }} for {{ parent_name_lower }}')),
			'READ_RELATED_OBJECT': Template(basic_listener_methods_base % ('Read{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}', 'read {{ name_lower }} for {{ parent_name_lower }}')),
			'UPDATE_RELATED': Template(basic_listener_methods_base % ('Update{{ name }}For{{ parent_name }}', '{{ name }} {{ name_lower }}', 'update {{ name_lower }} for {{ parent_name_lower }}')),
			'RemoveDuplicates': True,
			'Sort': True
		}

		methods_mapping = {
			'READ_OBJECT': Template(READ_OBJECT_METHOD),
			'READ_LIST': Template(READ_LIST_METHOD),
			'CREATE': Template(CREATE_METHOD),
			'UPDATE': Template(UPDATE_METHOD),
			'DELETE': Template(DELETE_METHOD),
			'READ_RELATED_LIST': Template(READ_RELATED_LIST_METHOD),
			'CREATE_RELATED': Template(CREATE_RELATED_METHOD),
			'READ_RELATED_OBJECT': Template(READ_RELATED_OBJECT_METHOD),
			'UPDATE_RELATED': Template(UPDATE_RELATED_METHOD)
		}

		response_cases_mapping = {
			'READ_OBJECT': Template(READ_OBJECT_RESPONSE),
			'READ_LIST': Template(READ_LIST_RESPONSE),
			'CREATE': Template(CREATE_RESPONSE),
			'UPDATE': Template(UPDATE_RESPONSE),
			'DELETE': Template(DELETE_RESPONSE),
			'READ_RELATED_LIST': Template(READ_RELATED_LIST_RESPONSE),
			'CREATE_RELATED': Template(CREATE_RELATED_RESPONSE),
			'READ_RELATED_OBJECT': Template(READ_RELATED_OBJECT_RESPONSE),
			'UPDATE_RELATED': Template(UPDATE_RELATED_RESPONSE),
			'RemoveDuplicates': True
		}

		# Set the configuration options and render
		self.mappings = {'urls': urls_mapping, 'request_types': request_types_mapping, 'listener_method_decls': listener_method_decls_mapping, 'basic_listener_methods': basic_listener_methods_mapping, 'methods': methods_mapping, 'response_cases': response_cases_mapping}
		self.object_id_format = "%d"
		self.slug_format = "%s"
		self.object_id_type = "int"
		self.slug_type = "String"
		self.render(*args, **options)
