static inline struct ecs_state *ecs_find_state(struct ecs_sensor *snsr, \
								int state_id)
{
	struct ecs_state *state = NULL;

	if (unlikely((state_id < 0) || (state_id >= snsr->state_num))) {
		x_log(2, "state %d not found", state_id);
		return NULL;
	}
	state = snsr->state_tab + state_id;
	if (unlikely(state_id == state->id))
		return state;
	x_log(3, "setting '%s' is the %dth state, but it's id is %d, " \
		"suggest move it to the %dth in state list to avoid bug", \
		state->name, state_id, state->id, state->id);
	return NULL;
}
/* ecs_find_xxx */
static inline struct ecs_property *ecs_find_property(struct ecs_sensor *snsr, \
								int prop_id)
{
	struct ecs_property *prop = NULL;

	if (unlikely((prop_id < 0) || (prop_id >= snsr->prop_num))) {
		x_log(2, "property %d not found", prop_id);
		return NULL;
	}
	prop = snsr->prop_tab + prop_id;
	if (unlikely(prop->id == prop_id))
		return prop;
	x_log(3, "property '%s' is the %dth property, but it's id is %d, " \
		"suggest move it to the %dth in property list to avoid bug", \
		prop->name, prop_id, prop->id, prop->id);
	return NULL;
}
int ecs_sensor_init(struct ecs_sensor *snsr)
{
	int i, j, ret;

	if (unlikely(snsr == NULL))
		return -EINVAL;

	if (snsr->hw_ctx == NULL) {
		x_log(0, "hardware context not defined");
		return -EINVAL;
	}
	if (snsr->cfg_handler == NULL) {
		x_inf(0, "config handler not defined");
		return -EINVAL;
	}

	/* by this time, sensor driver should have line up all the settings and
	 * propertys. ECS driver will check if all of them are well organized */
	for (i = 0; i < snsr->prop_num; i++) {
		struct ecs_property *prop = ecs_find_property(snsr, i);
		if (unlikely(prop == NULL)) {
			x_inf(0, "error checking property %d", i);
			return -EINVAL;
		}

		/* copy handlers by the way. If no specific handler defined for
		 * this property, use global default handler */
		if (prop->cfg_handler == NULL)
			prop->cfg_handler = snsr->cfg_handler;

		for (j = 0; j < prop->stn_num; j++) {
			struct ecs_setting *stn = ecs_find_setting(prop, j);
			if (unlikely(stn == NULL)) {
				x_inf(0, "error checking '%s' setting %d", \
								prop->name, j);
				return -EINVAL;
			}
		}
		prop->speculate = prop->speculate && snsr->speculate;
	}

	for (i = 1; i < snsr->state_num; i++) {
		struct ecs_state *state = ecs_find_state(snsr, i);
		if (unlikely(state == NULL)) {
			x_inf(0, "error checking state %d", i);
			return -EINVAL;
		}
	}
ecs_sensor_dump(snsr);
	/* from this point on, ecs can directly access property/setting/state
	 * rather than calling ecs_find_property/setting/state */
	return ret;
}
static inline struct ecs_setting *ecs_find_setting(struct ecs_property *prop, \
								int value)
{
	struct ecs_setting *stn = NULL;

	if (unlikely(prop == NULL))
		return NULL;
	if (unlikely((value < 0) || (value >= prop->stn_num))) {
		x_log(2, "setting %d not found", value);
		return NULL;
	}
	if (prop->stn_tab == NULL) {
		x_log(2, "no setting table defined for %s", prop->name);
		return NULL;
	}

	stn = prop->stn_tab + value;
	if (value == stn->id)
		return stn;
	x_log(3, "setting '%s' is the %dth setting, but it's id is %d, " \
		"suggest move it to the %dth in setting list to avoid bug", \
		stn->name, value, stn->id, stn->id);
	return NULL;
}
int ecs_set_list(struct ecs_sensor *snsr, \
					struct ecs_state_cfg *list, int len)
{
	int i, ret = 0;

	if ((snsr == NULL) || (list == NULL) || (len == 0))
		return -EINVAL;

	for (i = 0; i < len; i++) {
		int cnt;
		cnt = ecs_set_value(snsr, list);
		if (cnt < 0) {
			x_log(2, "set state failed");
			return -EIO;
		}
		ret += cnt;
		list++;
	}

	return ret;
}
int main(int argc, char **argv) {
    struct sockaddr_in config, peer;
    struct epoll_event ev, events[EP_SIZE];
    struct conn_status status[MAX_FD], *curr;

    in_port_t port = L_PORT;
    int listenfd, epfd, connfd;
    int i, flag, nfds, current_fd;
    socklen_t len;

    char buffer[FILE_BUFFER];

    // init
    sockinit(&config);
    sockset(&config, port, NULL);
    memset(status, 0, sizeof(status));
    memset(buffer, 0, sizeof(buffer));
    signal(SIGPIPE, SIG_IGN);

    // create server
    listenfd = create_server(&config);
    if (listenfd < 0) {
        perror("xtrans: server create failed.");
        exit(EXIT_FAILURE);
    }

    // create epoll fd
    epfd = ep_ini();
    if (epfd < 0) {
        perror("xtrans: epoll init failed.");
        exit(EXIT_FAILURE); 
    }

    // add listen fd to epoll
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    if (ep_add(epfd, listenfd, &ev) == -1) {
        perror("xtrans: add epoll event failed.");
        exit(EXIT_FAILURE);
    }

    // main loop
    for(;;) {
        nfds = ep_col(epfd, events);

        for (i = 0; i < nfds; i++) {
            current_fd = events[i].data.fd;

            if (current_fd == listenfd) {
                connfd = accept(listenfd, (SA *)&peer, &len);
                if (connfd == -1) {
                    continue;
                }
                memcpy(&(status[connfd].client), &peer, sizeof(struct sockaddr_in));

                ev.events = EPOLLIN;
                ev.data.fd = connfd;
                if (ep_add(epfd, connfd, &ev) == -1) {
                    continue;
                }
                
                flag = welcome(connfd, &(status[connfd]));
                if (flag >= 0) {
                    x_log(LOG_CONN, &(status[connfd]));
                } else {
                    x_log(LOG_DISC, &(status[connfd]));
                    terminate(epfd, connfd, &(status[connfd]));
                }

            } else if (events[i].events & EPOLLIN) {
                curr = &(status[current_fd]);

                switch (curr->status) {
                    case STATUS_INIT:
                        memset(&(curr->username), 0, sizeof(char)*12);

                        flag = readln(current_fd, curr->username, 1); 
                        if (flag < 0) {
                            terminate(epfd, current_fd, curr);
                            x_log(LOG_DISC, curr);
                        }

                        flag = get_username(current_fd, curr);
                        if (flag < 0) {
                            terminate(epfd, current_fd, curr);
                            x_log(LOG_DISC, curr);
                        }

                        break;
                    case STATUS_NAME:
                        memset(&(curr->passwd), 0, sizeof(char)*32);

                        flag = readln(current_fd, curr->passwd, 1);
                        if (flag < 0) {
                            terminate(epfd, current_fd, curr);
                            x_log(LOG_DISC, curr);
                        }

                        flag = get_passwd(current_fd, curr);
                        if (flag < 0) {
                            terminate(epfd, current_fd, curr);
                            x_log(LOG_DISC, curr);
                        }
                        
                        flag = auth(curr->username, curr->passwd);
                        if (flag < 0) {
                            flag = auth_failed(current_fd, curr);
                            terminate(epfd, current_fd, curr);

                            x_log(LOG_REFS, curr);
                        } else {
                            flag = auth_success(current_fd, curr);
                            if (flag < 0) {
                                x_log(LOG_DISC, curr);
                            } else {
                                x_log(LOG_LOGI, curr);
                            }
                        }

                        break;
                    case STATUS_SUCC:
                        flag = readFd(current_fd, buffer, 1024);
                        if (flag < 0) {
                            terminate(epfd, current_fd, curr);
                            x_log(LOG_DISC, curr);
                        }

                        curr->recvb = flag;
                        terminate(epfd, current_fd, curr);
                        x_log(LOG_FINS, curr);

                        break;
                    default:
                        perror("xtrans: status not define, default action triggered.");
                        terminate(epfd, current_fd, curr);
                }
            }
        }   
    }

    return 0;
}
int ecs_sensor_merge(struct ecs_sensor *orig, const struct ecs_sensor *plat)
{
	char *name;
	int i, j;

	/* if no speciman defined */
	if (plat == NULL)
		return 0;

	x_log(0, "merge '%s' into '%s'", plat->name, orig->name);
	name = (plat->name == NULL) ? ECS_DEFAULT_SENSOR_NAME : plat->name;
	if (orig->name == NULL)
		orig->name = name;

	/* allow speculate only if both sensor physical nature and
	 * sensor implementation allows speculate */
	orig->speculate &= plat->speculate;

	if ((orig->hw_ctx == NULL) && (plat->hw_ctx != NULL))
		orig->hw_ctx = plat->hw_ctx;

	if ((orig->cfg_handler == NULL) && (plat->cfg_handler != NULL))
		orig->cfg_handler = plat->cfg_handler;

	/* deal with the platform specific setting and property */
	for (i = 0; i < plat->prop_num; i++) {
		struct ecs_property *prop = ecs_find_property(orig, \
			plat->prop_tab[i].id);

		if (prop == NULL) {
			x_inf(0, "can't find property id(%d) in %s", \
				plat->prop_tab[i].id, orig->name);
			return -EINVAL;
		}

		x_log(0, "set %s as %s", prop->name, plat->prop_tab[i].name);
		if (plat->prop_tab[i].name != NULL)
			prop->name = plat->prop_tab[i].name;

		if (prop->stn_tab == NULL) {
			/* If original template has no setting table at all,
			 * just simply use the platform specific table */
			prop->stn_tab = plat->prop_tab[i].stn_tab;
			prop->stn_num = plat->prop_tab[i].stn_num;
			goto next;
		}
		/* deal with settings */
		for (j = 0; j < plat->prop_tab[i].stn_num; j++) {
			struct ecs_setting *stn = ecs_find_setting(prop, \
				plat->prop_tab[i].stn_tab[j].id);
			if (stn == NULL) {
				x_inf(0, "can't find setting id(%d) in %s", \
					plat->prop_tab[i].stn_tab[j].id, \
					prop->name);
				return -EINVAL;
			}
			stn->cfg_tab = plat->prop_tab[i].stn_tab[j].cfg_tab;
			stn->cfg_sz = plat->prop_tab[i].stn_tab[j].cfg_sz;
		}
next:
		if (plat->prop_tab[i].cfg_handler != NULL)
			prop->cfg_handler = plat->prop_tab[i].cfg_handler;
	}

	/* finally...states */
	/* deal with the platform specific setting and property */
	for (i = 0; i < plat->state_num; i++) {
		struct ecs_state *state = ecs_find_state(orig, \
			plat->state_tab[i].id);
		if (state == NULL) {
			x_inf(0, "can't find state id(%d) in template", \
				plat->state_tab[i].id);
			return -EINVAL;
		}

		state->cfg_tab = plat->state_tab[i].cfg_tab;
		state->cfg_num = plat->state_tab[i].cfg_num;
	}

	return 0;
}