# coding: UTF-8
# Coder for Japanese grid square code. (JIS C 6304 / JIS X 0410)
# 行政管理庁告示第143号 http://www.stat.go.jp/data/mesh/

def _encode_i2c(lat, lon, base1):
	t=[]
	while base1>80:
		t.append(1 + (lat&1)*2 + (lon&1))
		lat = lat>>1
		lon = lon>>1
		base1 = base1>>1
	
	if base1==80:
		t.append(lon%10)
		t.append(lat%10)
		lat = int(lat/10)
		lon = int(lon/10)
		base1 = int(base1/10)
	elif base1==16: # Uni5
		t.append(1 + (lat&1)*2 + (lon&1))
		lat = lat>>1
		lon = lon>>1
		base1 = base1>>1
	elif base1==40: # Uni2
		t.append(5)
		t.append(lon%5*2)
		t.append(lat%5*2)
		lat = int(lat/5)
		lon = int(lon/5)
		base1 = int(base1/5)
	
	if base1==8:
		t.append(lon%8)
		t.append(lat%8)
		lat = lat>>3
		lon = lon>>3
		base1 = base1>>3
	
	t.append(lon)
	t.append(lat)
	t.reverse()
	return ''.join([str(i) for i in t])

def encode(latitude, longitude, base1=80):
	return _encode_i2c(int(latitude*base1*1.5), int(longitude*base1-100.0*base1), base1)

#def _encode_i2c(lat, lon, base1):
def _decode_c2i(gridcode):
	base1 = 1
	lat = lon = 0
	codelen = len(gridcode)
	if codelen>0:
		lat = int(gridcode[0:2])
		lon = int(gridcode[2:4])
	
	if codelen>4:
		lat = (lat<<3) + int(gridcode[4:5])
		lon = (lon<<3) + int(gridcode[5:6])
		base1 = base1<<3
	
	if codelen>6:
		if codelen==7:
			i = int(gridcode[6:7])-1
			lat = (lat<<1) + int(i/2)
			lon = (lon<<1) + i%2
			base1 = base1<<1
		else:
			lat = lat*10 + int(gridcode[6:7])
			lon = lon*10 + int(gridcode[7:8])
			base1 = base1*10
	
	if codelen>8:
		if gridcode[8:]=='5':
			lat = lat>>1
			lon = lon>>1
			base1 = base1>>1
		else:
			for i in gridcode[8:]:
				i = int(i)-1
				lat = (lat<<1) + int(i/2)
				lon = (lon<<1) + i%2
				base1 = base1<<1
	
	return (lat, lon, base1)

def decode_sw(gridcode, delta=False):
	(lat, lon, base1) = _decode_c2i(gridcode)
	
	lat = lat/(base1*1.5)
	lon = lon/float(base1) + 100.0
	
	if delta:
		return (lat, lon, 1.0/(base1*1.5), 1.0/base1)
	else:
		return (lat, lon)

def decode(gridcode):
	(lat, lon, base1) = _decode_c2i(gridcode)
	
	# center position of the meshcode.
	lat = (lat<<1) + 1
	lon = (lon<<1) + 1
	base1 = base1<<1
	return (lat/(base1*1.5), lon/float(base1) + 100.0)

def bbox(gridcode):
	(a,b,c,d) = decode_sw(gridcode, True)
	return {'w':a, 's':b, 'n':b+d, 'e':a+c}


## short-cut methods
def encodeLv1(lat, lon):
	return encode(lat,lon,1)

def encodeLv2(lat, lon):
	return encode(lat,lon,8)

def encodeLv3(lat, lon):
	return encode(lat,lon,80)

def encodeBase(lat,lon):
	return encodeLv3(lat,lon)

def encodeHalf(lat,lon):
	return encode(lat,lon,160)

def encodeQuarter(lat,lon):
	return encode(lat,lon,320)

def encodeEighth(lat,lon):
	return encode(lat,lon,640)

def encodeUni10(lat,lon):
	return encodeLv2(lat,lon)

def encodeUni5(lat, lon):
	return encode(lat,lon,16)

def encodeUni2(lat, lon):
	return encode(lat,lon,40)


def neighbors(gridcode):
	(lat,lon,base1)=_decode_c2i(gridcode)
	ret = []
	for i in ((0,-1),(0,1),(1,-1),(1,0),(1,1),(-1,-1),(-1,0),(-1,1)):
		tlat=lat+i[0]
		tlon=lon+i[1]
		if tlat<0 or tlat>(90*base1):
			continue
		if tlon<0 or tlon>(100*base1):
			continue
		
		ret.append(_encode_i2c(tlat,tlon,base1))
	
	return ret

def expand(gridcode):
	ret = neighbors(gridcode)
	ret.append(gridcode)
	return ret
