#include <AsyncJpegStream.h>

static const char* STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* STREAM_PART = "Content-Type: %s\r\nContent-Length: %u\r\n\r\n";

static const char * JPG_CONTENT_TYPE = "image/jpeg";

AsyncJpegStreamResponse::AsyncJpegStreamResponse(){
    _callback = nullptr;
    _code = 200;
    _contentLength = 0;
    _contentType = STREAM_CONTENT_TYPE;
    _sendContentLength = false;
    _chunked = true;
    _index = 0;
    _jpg_buf_len = 0;
    _jpg_buf = NULL;
    lastAsyncRequest = 0;
    memset(&_frame, 0, sizeof(camera_frame_t));
}

AsyncJpegStreamResponse::~AsyncJpegStreamResponse(){
    if(_frame.fb){
        if(_frame.fb->format != PIXFORMAT_JPEG){
            free(_jpg_buf);
        }
        esp_camera_fb_return(_frame.fb);
    }
}

/*bool AsyncJpegStreamResponse::_sourceValid() {
    return true;
}*/

size_t AsyncJpegStreamResponse::_fillBuffer(uint8_t *buf, size_t maxLen) {
    size_t ret = _content(buf, maxLen, _index);
    if(ret != RESPONSE_TRY_AGAIN){
        _index += ret;
    }
    return ret;
}

size_t AsyncJpegStreamResponse::_content(uint8_t *buffer, size_t maxLen, size_t index){
    if(!_frame.fb || _frame.index == _jpg_buf_len) {
        if(index && _frame.fb){
            uint64_t end = (uint64_t)micros();
            int fp = (end - lastAsyncRequest) / 1000;
            log_printf("Size: %uKB, Time: %ums (%.1ffps)\n", _jpg_buf_len/1024, fp);
            lastAsyncRequest = end;
            if(_frame.fb->format != PIXFORMAT_JPEG){
                free(_jpg_buf);
            }
            esp_camera_fb_return(_frame.fb);
            _frame.fb = NULL;
            _jpg_buf_len = 0;
            _jpg_buf = NULL;
        }
        if(maxLen < (strlen(STREAM_BOUNDARY) + strlen(STREAM_PART) + strlen(JPG_CONTENT_TYPE) + 8)){
            //log_w("Not enough space for headers");
            return RESPONSE_TRY_AGAIN;
        }
        //get frame
        _frame.index = 0;

        _frame.fb = esp_camera_fb_get();
        if (_frame.fb == NULL) {
            log_e("Camera frame failed");
            return 0;
        }

        if(_frame.fb->format != PIXFORMAT_JPEG){
            unsigned long st = millis();
            bool jpeg_converted = frame2jpg(_frame.fb, 80, &_jpg_buf, &_jpg_buf_len);
            if(!jpeg_converted){
                log_e("JPEG compression failed");
                esp_camera_fb_return(_frame.fb);
                _frame.fb = NULL;
                _jpg_buf_len = 0;
                _jpg_buf = NULL;
                return 0;
            }
            log_i("JPEG: %lums, %uB", millis() - st, _jpg_buf_len);
        } else {
            _jpg_buf_len = _frame.fb->len;
            _jpg_buf = _frame.fb->buf;
        }

        //send boundary
        size_t blen = 0;
        if(index){
            blen = strlen(STREAM_BOUNDARY);
            memcpy(buffer, STREAM_BOUNDARY, blen);
            buffer += blen;
        }
        //send header
        size_t hlen = sprintf((char *)buffer, STREAM_PART, JPG_CONTENT_TYPE, _jpg_buf_len);
        buffer += hlen;
        //send frame
        hlen = maxLen - hlen - blen;
        if(hlen > _jpg_buf_len){
            maxLen -= hlen - _jpg_buf_len;
            hlen = _jpg_buf_len;
        }
        memcpy(buffer, _jpg_buf, hlen);
        _frame.index += hlen;
        return maxLen;
    }

    size_t available = _jpg_buf_len - _frame.index;
    if(maxLen > available){
        maxLen = available;
    }
    memcpy(buffer, _jpg_buf+_frame.index, maxLen);
    _frame.index += maxLen;

    return maxLen;
}


void streamJpg(AsyncWebServerRequest *request){
    AsyncJpegStreamResponse *response = new AsyncJpegStreamResponse();
    if(!response){
        request->send(501);
        return;
    }
    request->send(response);
}