# A name service: register human-readable names that point to addresses.
# Names are rented by the year; an expired name can be registered again.
contract NameService

const PRICE_PER_YEAR: int = TCN / 10   # 0.1 TCN
const YEAR: int = 525_600              # blocks (1 block = 1 minute)
const MAX_YEARS: int = 10

state admin: address
state owner_of: map[text, address]
state target_of: map[text, address]
state expires_at: map[text, int]

event Registered(name: text, owner: address, expires: int)
event Renewed(name: text, expires: int)
event Transferred(name: text, from: address, to: address)
event Pointed(name: text, target: address)

init():
    admin = caller

action register(name: text, years: int) payable:
    require valid_name(name), "names have 3 to 32 characters: a-z, 0-9 and '-'"
    require years >= 1 and years <= MAX_YEARS, "register for 1 to 10 years"
    require value == PRICE_PER_YEAR * years, "send exactly 0.1 TCN per year"
    require not is_active(name), "this name is taken"
    owner_of[name] = caller
    target_of[name] = caller
    expires_at[name] = height + YEAR * years
    emit Registered(name, caller, expires_at[name])

# Anyone may pay to renew a name (for example as a gift), but it stays with its owner.
action renew(name: text, years: int) payable:
    require is_active(name), "name not registered or expired"
    require years >= 1 and years <= MAX_YEARS, "renew for 1 to 10 years"
    require value == PRICE_PER_YEAR * years, "send exactly 0.1 TCN per year"
    let new_expiry: int = expires_at[name] + YEAR * years
    require new_expiry <= height + YEAR * MAX_YEARS, "at most 10 years ahead"
    expires_at[name] = new_expiry
    emit Renewed(name, new_expiry)

action transfer(name: text, to: address):
    only_name_owner(name)
    owner_of[name] = to
    emit Transferred(name, caller, to)

action point_to(name: text, target: address):
    only_name_owner(name)
    target_of[name] = target
    emit Pointed(name, target)

action collect_fees():
    require caller == admin, "only the admin"
    require balance > 0, "nothing to collect"
    send(admin, balance)

view resolve(name: text) -> address:
    require is_active(name), "name not registered or expired"
    return target_of[name]

view whois(name: text) -> address:
    require is_active(name), "name not registered or expired"
    return owner_of[name]

view expires(name: text) -> int:
    return expires_at[name]

view available(name: text) -> bool:
    return valid_name(name) and not is_active(name)

fn only_name_owner(name: text):
    require is_active(name), "name not registered or expired"
    require owner_of[name] == caller, "you do not own this name"

fn is_active(name: text) -> bool:
    return owner_of.has(name) and expires_at[name] >= height

fn valid_name(name: text) -> bool:
    let n: int = len(name)
    if n < 3 or n > 32:
        return false
    let b: bytes = to_bytes(name)
    for i in range(0, n):
        let c: int = b[i]
        let letter: bool = c >= 97 and c <= 122
        let digit: bool = c >= 48 and c <= 57
        if not (letter or digit or c == 45):
            return false
    return true
