#-*- coding: utf-8 -*-

# Copyright 2008-2010 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import math
from cl_utils import process, checkUtils, readFile, listDirectory, \
                     readLinesFile, getRunCommands
import sys
import re
from os import path

import cl_lang
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])

# ip digit from 0|1-255|254 (template)
IP_DIG = "[%s-9]|(?:1[0-9]|[1-9])[0-9]|2[0-4][0-9]|25[0-%s]"
# ip net 0-32
IP_NET_SUFFIX = "[0-9]|[12][0-9]|3[012]"
# ip digs 1-254,0-254,0-255
IP_DIGS = { 'dig1_254' : IP_DIG % (1,4), 'dig0_254' : IP_DIG % (0,4),
           'dig0_255' : IP_DIG % (0,5), }
# ip addr 10.0.0.12
IP_ADDR = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig1_254)s)"%\
            IP_DIGS
IP_MASK = "(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)"%\
            IP_DIGS
# ip addr for net 10.0.0.0
IP_NET = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)"%\
            IP_DIGS
# ip and net 192.168.0.0/16
IP_ADDR_NET = "(%(ipaddr)s)/((%(ipnet)s))"%{'ipaddr':IP_NET,
                                            'ipnet':IP_NET_SUFFIX}

reIp = re.compile("^{0}$".format(IP_ADDR))
reNetSuffix = re.compile("^{0}$".format(IP_NET_SUFFIX))
reNet = re.compile("^{0}$".format(IP_ADDR_NET))
reMask = re.compile("^{0}$".format(IP_MASK))

def checkIp(ip):
    """Check ip"""
    return reIp.match(ip)

def checkNetSuffix(netSuffix):
    """Check net suffix"""
    return reNetSuffix.match(netSuffix)

def checkNet(net):
    """Check net"""
    if not reNet.match(net):
        return False
    ip,op,cidr = net.partition('/')
    mask = strIpToIntIp(cidrToMask(int(cidr)))
    return (strIpToIntIp(ip)&mask) == (strIpToIntIp(ip))

maskDigs = map(lambda x:str(x),(0b10000000,0b11000000,0b11100000,0b11110000,
                                0b11111000,0b11111100,0b11111110,0b11111111))

def checkMask(mask):
    """Check net"""
    if mask.count('.') != 3:
        return False
    zero = False
    for dig in mask.split('.'):
        if zero or not dig in maskDigs:
            if dig == "0":
                zero = True
            else:
                return False
    return True

def getIpAndMask(interface="eth0"):
    """Get ip and mask from interface"""
    ifconfig = process('/sbin/ifconfig',interface)
    res = re.search(r"inet addr:(\S+)\s.*Mask:(\S+)",ifconfig.read(),re.S)
    if res:
        return res.groups()
    else:
        return ("","")

def strIpToIntIp(addr):
    """Convert ip specified by string to integer"""
    addr = addr.split('.')
    return ((int(addr[0])<<24)|
           (int(addr[1])<<16)|
           (int(addr[2])<<8)|
           (int(addr[3])))
    return reduce(lambda x,y:x+(int(y[1])<<(y[0]*8)),
           enumerate(reversed(addr.split("."))),0)

def intIpToStrIp(addr):
    """Convert ip specified by integer to string"""
    return "{0}.{1}.{2}.{3}".format(
            addr>>24,(addr>>16)&0xff,(addr>>8)&0xff,addr&0xff)

def maskToCidr(mask):
    """Convert mask specified by str to net"""
    mask = strIpToIntIp(mask)
    return 32-int(math.log(((~mask) & 0xffffffff)+1,2))

def cidrToMask(cidr):
    """Convert net to mask specified by str"""
    return intIpToStrIp((2**cidr-1)<<(32-cidr))

def getIpNet(ip,mask=None,cidr=None):
    """Get net (xx.xx.xx.xx/xx) by ip address and mask"""
    ip = strIpToIntIp(ip)
    if not mask is None:
        net = maskToCidr(mask)
    else:
        net = int(cidr)
        mask = cidrToMask(net)
    mask = strIpToIntIp(mask)
    return "{ip}/{net}".format(ip=intIpToStrIp(ip&mask),
                               net=net)

def isIpInNet(checkip,*ipnets):
    """Check is ip in specified nets"""
    return map(lambda x:x[0],
          filter(lambda x:strIpToIntIp(checkip)&x[2] == strIpToIntIp(x[1])&x[2],
          map(lambda x:(x[0],x[1][0],strIpToIntIp(cidrToMask(int(x[1][1])))),
          map(lambda x:(x,x.partition('/')[0::2]),
          ipnets))))

def receiveMac(interface="eth0"):
    """Get MAC from interface"""
    ipconfigProg = checkUtils('/sbin/ifconfig')
    ifconfig = process(ipconfigProg,interface)
    res = re.search(r"HWaddr\s(\S+)",ifconfig.read(),re.S)
    if res:
        return res.group(1)
    else:
        return "00:00:00:00:00:00"

def receiveIpAndMask(interface="eth0"):
    """Get ip and mask from interface"""
    ipconfigProg = checkUtils('/sbin/ifconfig')
    ifconfig = process(ipconfigProg,interface)
    res = re.search(r"inet addr:(\S+)\s.*Mask:(\S+)",ifconfig.read(),re.S)
    if res:
        return res.groups()
    else:
        return ("","")

def isDhcpIp(interface="eth0"):
    """Get ip by dhcp or static"""
    # dhclient
    if filter(lambda x:interface in x and ("dhcpcd" in x or "dhclient" in x),
       getRunCommands()):
        return True
    else:
        return False

def getRouteTable(onlyIface=[]):
    """Get route table, exclude specifed iface"""
    ipProg = checkUtils('/sbin/ip')
    routes = process(ipProg,"route")
    if onlyIface:
        filterRe = re.compile("|".join(map(lambda x:r"dev %s"%x,onlyIface)))
        routes = filter(filterRe.search,routes)
    for line in routes:
        network,op,line = line.partition(" ")
        routeParams = map(lambda x:x.strip(),line.split())
        # (network,{'via':value,'dev':value})
        if network:
            yield (network,dict(zip(routeParams[0::2],routeParams[1::2])))

def getInterfaces():
    """Get available interfaces (discard which hasn't device)"""
    sysNet = "/sys/class/net"
    return filter(lambda x:path.exists(path.join(sysNet,x,"device")),
           listDirectory(sysNet))
