import noise
import random
from collections import defaultdict
import math

class Map():

  def __init__( self, width=150, height=50, sealevel=50, scale=32, base=random.randint(1,250), lacunarity=0.5+random.random(), octaves=50, persistence=0.5, xoffset=random.randint(1,10000), yoffset=random.randint(1,10000) ):

    print("Initializing map")

    # Dealing with basic params
    self.width = width
    self.height = height
    self.sealevel = -0.37+((0.75/100)*sealevel)
    self.scale = scale
    self.base = base
    self.octaves = octaves
    self.persistence = persistence
    self.lacunarity = lacunarity
    self.xoffset = xoffset
    self.yoffset = yoffset

    # The master map we are going to need
    self.map = defaultdict(dict)
    self.coast = []

    # All the required stuff to determine cities
    self.cities = []
    # self.num_cities = 50+(100-int(self.sealevel))
    self.num_cities = 75
    self.coast_percentage = 25
    self.distance = 3


  def generateMap(self):
    print("Generating Map")
    # Fill in the blanks with random stuff
    for x in range(1,self.width+1):
      for y in range(1,self.height+1):
        area = noise.pnoise2( self.xoffset+x/self.scale, self.yoffset+y/(self.scale/3), octaves=self.octaves, persistence=self.persistence, lacunarity=self.lacunarity, repeatx=self.height, repeaty=self.width, base=self.base )
        if area > self.sealevel:
          self.map[x][y] = "l"
        else:
          self.map[x][y] = "w"


  # Find coastal positions
  def findCoast(self):
    print("Finding coastal tiles")
    # Should replace with a range()-loop
    y = 1

    while y <= self.height:
      x = 1
      while x <= self.width:
        water = 0
        if x !=1 and x!=self.width and y!=1 and y!=self.height and self.map[x][y]=="l":
          if self.map[x-1][y-1]=="w":
            water += 1
          if self.map[x][y-1]=="w":
            water += 1
          if self.map[x+1][y-1]=="w":
            water += 1
          if self.map[x+1][y]=="w":
            water += 1
          if self.map[x+1][y+1]=="w":
            water += 1
          if self.map[x][y+1]=="w":
            water += 1
          if self.map[x-1][y+1]=="w":
            water += 1
          if self.map[x-1][y]=="w":
            water += 1
        if water >= 1:
          coords = {}
          coords['x'] = x
          coords['y'] = y
          self.coast.append(coords)
        x+=1
      y+=1


  def generateCities(self):
    print("Generate cities")
    for round in range(0,self.num_cities):
      if random.randint(1,100) < self.coast_percentage:
        self.cities.append(random.choice(self.coast))
      else:
        found = 0
        while found == 0:
          randX = random.randint(1,self.width)
          randY = random.randint(1,self.height)
          if self.map[randX][randY]=="l" and self.noCityNear(self.distance,randX,randY):
            found = 1
            coords = {}
            coords['x'] = randX
            coords['y'] = randY
            self.cities.append(coords)


  def noCityNear(self,distance,x,y):
    noCityNear = True
    counter = 0
    for city in self.cities:
      #Lay-Z D-Bug: print( str(counter) +" x:"+ str( abs(city['x']-x) ) +"; y:"+ str( abs(city['y']-y) ) +";")
      # This should be an OR, but this takes forever! So this is good enough.
      if abs(city['x']-x)<self.distance and abs(city['y']-y)<self.distance:
        noCityNear = False
      counter+=1
    return noCityNear


  def calcDistance(self,city,x,y):
    return math.ceil( math.sqrt( ((city['x']-x)**2)+((city['y']-y)**2) ) )


  def addCities(self):
    print("Adding cities to map")
    for city in self.cities:
      self.map[city['x']][city['y']] = "c"


  # Print the world in its entirety
  # Per column we print the entire row
  def printWorld(self):
    print("Printing map: num_cities:"+str(self.num_cities)+" base:"+str(self.base)+" Offset:"+str(self.xoffset)+"/"+str(self.yoffset))
    for y in range(1,self.height+1):
      for x in range(1,self.width+1):
        if self.map[x][y] == "c":
          print('\x1b[1;37;42m' + '*' + '\x1b[0m', end='')
        elif self.map[x][y] == "l":
          print('\x1b[1;37;42m' + ' ' + '\x1b[0m', end='')
        else:
          print('\x1b[1;37;44m' + ' ' + '\x1b[0m', end='')
      print("")

  # Dump the map that Python can work with
  def dumpMap(self):
    terrain = self.map
    metadata = {}
    metadata['width'] = self.width
    metadata['height'] = self.height
    metadata['coast_percentage'] = self.coast_percentage
    metadata['scale'] = self.scale
    metadata['base'] = self.base
    metadata['octaves'] = self.octaves
    metadata['persistence'] = self.persistence
    metadata['lacunarity'] = self.lacunarity
    metadata['xoffset'] = self.xoffset
    metadata['yoffset'] = self.yoffset
    return terrain, self.cities, self.coast, metadata