// Copyright 2011 Xing Xing <mikespook@gmail.com> All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package gearman

import (
    "net"
    "os"
//    "log"
)

// The client of job server.
type jobClient struct {
    conn net.Conn
    worker *Worker
    running bool
    incoming chan []byte
}

// Create the client of job server.
func newJobClient(addr string, worker *Worker) (jobclient *jobClient, err os.Error) {
    conn, err := net.Dial(TCP, addr)
    if err != nil {
        return nil, err
    }
    jobclient = &jobClient{conn:conn, worker:worker, running:true, incoming: make(chan []byte, QUEUE_CAP)}
    return jobclient, err
}

// Internal read
func (client *jobClient) read() (data []byte, err os.Error){
    if len(client.incoming) > 0 {
        // incoming queue is not empty
        data = <-client.incoming
    } else {
        for {
            buf := make([]byte, BUFFER_SIZE)
            var n int
            if n, err = client.conn.Read(buf); err != nil {
                if err == os.EOF && n == 0 {
                    err = nil
                    return
                }
                return
            }
            data = append(data, buf[0: n] ...)
            if n < BUFFER_SIZE {
                break
            }
        }
    }
    // split package
    start := 0
    tl := len(data)
    for i := 0; i < tl; i ++{
        if string(data[start:start+4]) == RES_STR {
            l := int(byteToUint32([4]byte{data[start+8], data[start+9], data[start+10], data[start+11]}))
            total := l + 12
            if total == tl {
                return
            } else {
                client.incoming <- data[total:]
                data = data[:total]
                return
            }
        } else {
            start++
        }
    }
    err = os.NewError("Invalid data struct.")
    return
}

// Main loop.
func (client *jobClient) Work() {
    noop := true
    for client.running {
        // got noop msg and incoming queue is zero, grab job
        if noop && len(client.incoming) == 0 {
            client.WriteJob(NewWorkerJob(REQ, GRAB_JOB, nil))
        }
        rel, err := client.read()
        if err != nil {
            client.worker.ErrQueue <- err
            continue
        }
        job, err := DecodeWorkerJob(rel)
        if err != nil {
            client.worker.ErrQueue <- err
            continue
        } else {
            switch(job.DataType) {
                case NOOP:
                    noop = true
                case NO_JOB:
                    noop = false
                    client.WriteJob(NewWorkerJob(REQ, PRE_SLEEP, nil))
                case ECHO_RES, JOB_ASSIGN_UNIQ, JOB_ASSIGN:
                    job.client = client
                    client.worker.incoming <- job
            }
        }
    }
    return
}

// Send a job to the job server.
func (client *jobClient) WriteJob(job *WorkerJob) (err os.Error) {
    return client.Write(job.Encode())
}

// Write the encoded job.
func (client *jobClient) Write(buf []byte) (err os.Error) {
    var n int
    for i := 0; i < len(buf); i += n {
        n, err = client.conn.Write(buf[i:])
        if err != nil {
            return err
        }
    }
    return
}

// Close.
func (client *jobClient) Close() (err os.Error) {
    client.running = false
    close(client.incoming)
    err = client.conn.Close()
    return
}