#import "TGNeoRowController.h"
#import "TGNeoConversationRowController.h"
#import "TGNeoConversationSimpleRowController.h"
#import "TGNeoConversationMediaRowController.h"
#import "TGNeoConversationStaticRowController.h"

#import "TGStringUtils.h"
#import "TGLocationUtils.h"

#import "TGNeoMessageViewModel.h"
#import "TGNeoBubbleMessageViewModel.h"
#import "TGNeoStickerMessageViewModel.h"

#import "TGBridgeMessage.h"

#import "WKInterfaceGroup+Signals.h"
#import "TGBridgeMediaSignals.h"

@interface TGNeoRowController ()
{
    TGNeoMessageViewModel *_viewModel;
    SMetaDisposable *_renderDisposable;
    
    bool _pendingRendering;
    
    bool _processing;
    NSString *_normalIconName;
    NSString *_processingIconName;
}
@end

@implementation TGNeoRowController

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        _renderDisposable = [[SMetaDisposable alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [_renderDisposable dispose];
}

+ (CGSize)containerSizeForMessage:(TGBridgeMessage *)message
{
    if (message.outgoing)
    {
        static dispatch_once_t onceToken;
        static CGSize containerSize;
        dispatch_once(&onceToken, ^
        {
            CGSize screenSize = [[WKInterfaceDevice currentDevice] screenBounds].size;
            containerSize = CGSizeMake(screenSize.width - 5, screenSize.height);
        });
        return containerSize;
    }
    else
    {
        static dispatch_once_t onceToken;
        static CGSize containerSize;
        dispatch_once(&onceToken, ^
        {
            containerSize = [[WKInterfaceDevice currentDevice] screenBounds].size;
        });
        return containerSize;
    }
}

- (void)updateWithMessage:(TGBridgeMessage *)message context:(TGBridgeContext *)context index:(NSInteger)index type:(TGNeoMessageType)type
{
    bool isChannelMessage = (type == TGNeoMessageTypeChannel);
    
    if (!isChannelMessage)
        [self updateStatusWithMessage:message];
    
    if ([self renderIfNeeded])
        return;
    
    if (_viewModel != nil)
        return;
    
    _viewModel = [TGNeoMessageViewModel viewModelForMessage:message type:type context:context additionalPeers:self.additionalPeers];
    
    CGSize containerSize = [TGNeoRowController containerSizeForMessage:message];
    CGSize contentSize = [_viewModel layoutWithContainerSize:containerSize];
    
    if (_viewModel.showBubble)
    {
        if (isChannelMessage)
        {
            [self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleChannel"];
        }
        else
        {
            if (message.outgoing)
                [self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleOutgoing"];
            else
                [self.bubbleGroup setBackgroundImageNamed:@"ChatBubbleIncoming"];
        }
    }
    
    if (!isChannelMessage && message.outgoing)
    {
        [self.bubbleGroup setHorizontalAlignment:WKInterfaceObjectHorizontalAlignmentRight];
        
        if (!_viewModel.showBubble)
            [self.statusGroup setContentInset:UIEdgeInsetsMake(4, 0, 0, 0)];
    }
    
    self.bubbleGroup.width = contentSize.width;
    self.bubbleGroup.height = contentSize.height;
    
    self.contentGroup.width = contentSize.width;
    self.contentGroup.height = contentSize.height;
    
    [self applyAdditionalLayoutForViewModel:_viewModel];
    
    bool shouldRender = true;
    if (self.shouldRenderContent != nil)
        shouldRender = self.shouldRenderContent();
    
    if (shouldRender)
        [self _render];
    else
        _pendingRendering = true;
}

- (void)_render
{
    _pendingRendering = false;
    
    bool onMainThread = true;    
    SSignal *signal = [TGNeoRenderableViewModel renderSignalForViewModel:_viewModel];
    if (!onMainThread)
        signal = [[signal startOn:[SQueue concurrentDefaultQueue]] deliverOn:[SQueue mainQueue]];
    
    __weak TGNeoRowController *weakSelf = self;
    [_renderDisposable setDisposable:[signal startWithNext:^(UIImage *image)
    {
        __strong TGNeoRowController *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        [strongSelf.contentGroup setBackgroundImage:image];
    }]];
}

- (bool)renderIfNeeded
{
    if (!_pendingRendering)
        return false;
    
    [self _render];
    
    return true;
}

- (void)updateStatusWithMessage:(TGBridgeMessage *)message
{
    if (message.outgoing)
    {
        bool failed = (message.deliveryState == TGBridgeMessageDeliveryStateFailed);
        bool unread = (message.deliveryState == TGBridgeMessageDeliveryStatePending || (message.deliveryState == TGBridgeMessageDeliveryStateDelivered && message.unread));

        bool dotHidden = !failed && !unread;
        self.statusGroup.hidden = dotHidden;

        if (!dotHidden)
        {
            if (failed)
                [self.statusIcon setTintColor:[UIColor hexColor:0xff4a5c]];
            else if (unread)
                [self.statusIcon setTintColor:[UIColor hexColor:0x2ba2e7]];
        }
    }
}

- (void)applyAdditionalLayoutForViewModel:(TGNeoMessageViewModel *)viewModel
{
    NSDictionary *layout = viewModel.additionalLayout;
    
    NSDictionary *headerGroupLayout = layout[TGNeoMessageHeaderGroup];
    if (headerGroupLayout != nil)
    {
        self.headerWrapperGroup.hidden = false;
        
        NSDictionary *imageLayout = headerGroupLayout[TGNeoMessageReplyImageGroup];
        if (imageLayout != nil)
        {
            TGBridgeMediaAttachment *attachment = imageLayout[TGNeoMessageReplyMediaAttachment];
            if (attachment != nil)
            {
                CGSize imageSize = CGSizeMake(26, 26);
                SSignal *imageSignal = [attachment isKindOfClass:[TGBridgeImageMediaAttachment class]] ? [TGBridgeMediaSignals previewWithImageAttachment:(TGBridgeImageMediaAttachment *)attachment size:imageSize] : [TGBridgeMediaSignals previewWithVideoAttachment:(TGBridgeVideoMediaAttachment *)attachment size:imageSize];
                
                [self.replyImageGroup setBackgroundImageSignal:imageSignal isVisible:self.isVisible];
            }
        }
        
        NSValue *insetValue = headerGroupLayout[TGNeoContentInset];
        if (insetValue != nil)
        {
            UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
            [self.headerWrapperGroup setContentInset:inset];
        }
    }
    
    NSDictionary *mediaGroupLayout = layout[TGNeoMessageMediaGroup];
    if (mediaGroupLayout != nil)
    {
        self.mediaWrapperGroup.hidden = false;
        
        if (![viewModel isKindOfClass:[TGNeoStickerMessageViewModel class]])
        {
            if (!viewModel.showBubble)
                [self.imageGroup setCornerRadius:13];
            else
                [self.imageGroup setCornerRadius:12];
        }
        
        NSDictionary *imageGroup = mediaGroupLayout[TGNeoMessageMediaImage];
        NSDictionary *mapGroup = mediaGroupLayout[TGNeoMessageMediaMap];
        if (imageGroup != nil)
        {
            self.imageGroup.hidden = false;
            TGBridgeMediaAttachment *attachment = imageGroup[TGNeoMessageMediaImageAttachment];
            
            CGSize size = CGSizeMake(100, 100);
            NSValue *imageSizeValue = imageGroup[TGNeoMessageMediaSize];
            if (imageSizeValue != nil)
            {
                size = imageSizeValue.CGSizeValue;
                self.imageGroup.width = size.width;
                self.imageGroup.height = size.height;
            }
            
            bool hasPlayButton = [imageGroup[TGNeoMessageMediaPlayButton] boolValue];
            bool hasSpinner = [imageGroup[TGNeoMessageMediaImageSpinner] boolValue];
            
            if (hasSpinner)
                self.spinnerImage.hidden = false;
            
            __weak TGNeoRowController *weakSelf = self;
            if ([attachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
            {
                [self.imageGroup setBackgroundImageSignal:[[TGBridgeMediaSignals previewWithImageAttachment:(TGBridgeImageMediaAttachment *)attachment size:size] onNext:^(id next)
                {
                    __strong TGNeoRowController *strongSelf = weakSelf;
                    if (strongSelf == nil)
                        return;
                    
                    strongSelf.spinnerImage.hidden = true;
                }] isVisible:self.isVisible];
            }
            else if ([attachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
            {
                [self.imageGroup setBackgroundImageSignal:[[TGBridgeMediaSignals previewWithVideoAttachment:(TGBridgeVideoMediaAttachment *)attachment size:size] onNext:^(id next)
                {
                    __strong TGNeoRowController *strongSelf = weakSelf;
                    if (strongSelf == nil)
                        return;
                                                               
                    strongSelf.spinnerImage.hidden = true;
                    if (hasPlayButton)
                        strongSelf.videoButton.hidden = false;
                }] isVisible:self.isVisible];
            }
            else if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
            {
                [self.imageGroup setBackgroundImageSignal:[TGBridgeMediaSignals stickerWithDocumentAttachment:(TGBridgeDocumentMediaAttachment *)attachment type:TGMediaStickerImageTypeNormal] isVisible:self.isVisible];
            }
        }
        else if (mapGroup != nil)
        {
            self.map.hidden = false;
            
            CGSize size = CGSizeMake(100, 100);
            NSValue *imageSizeValue = mapGroup[TGNeoMessageMediaSize];
            if (imageSizeValue != nil)
            {
                size = imageSizeValue.CGSizeValue;
                self.map.width = size.width;
                self.map.height = size.height;
            }
            
            NSValue *coordinateValue = mapGroup[TGNeoMessageMediaMapCoordinate];
            if (coordinateValue != nil)
            {
                CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue];
                CLLocationCoordinate2D regionCoordinate = CLLocationCoordinate2DMake([TGLocationUtils adjustGMapLatitude:coordinate.latitude withPixelOffset:-10 zoom:15], coordinate.longitude);
                
                self.map.region = MKCoordinateRegionMake(regionCoordinate, MKCoordinateSpanMake(0.003, 0.003));
                self.map.centerPinCoordinate = coordinate;
            }
        }
        
        NSValue *insetValue = mediaGroupLayout[TGNeoContentInset];
        if (insetValue != nil)
        {
            UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
            [self.mediaWrapperGroup setContentInset:inset];
        }
    }
    
    NSDictionary *metaGroupLayout = layout[TGNeoMessageMetaGroup];
    if (metaGroupLayout != nil)
    {
        self.metaWrapperGroup.hidden = false;
        
        NSDictionary *audioLayout = metaGroupLayout[TGNeoMessageAudioButton];
        if (audioLayout != nil)
        {
            self.audioButton.hidden = false;
            
            NSNumber *hasBackground = audioLayout[TGNeoMessageAudioButtonHasBackground] ?: @true;
            if (hasBackground.boolValue)
                [self.audioButtonGroup setBackgroundColor:[UIColor hexColor:0x6bbeee]];
            else
                [self.audioButtonGroup setBackgroundColor:[UIColor clearColor]];
            
            NSString *audioIcon = audioLayout[TGNeoMessageAudioIcon];
            if (audioIcon != nil)
            {
                _normalIconName = audioIcon;
                [self.audioIcon setImageNamed:audioIcon];
            }
            
            NSString *audioAnimatedIcon = audioLayout[TGNeoMessageAudioAnimatedIcon];
            if (audioAnimatedIcon != nil)
                _processingIconName = audioAnimatedIcon;
            
            UIColor *iconColor = audioLayout[TGNeoMessageAudioIconTint];
            if (iconColor != nil)
                [self.audioIcon setTintColor:iconColor];
        }
        
        NSDictionary *avatarLayout = metaGroupLayout[TGNeoMessageAvatarGroup];
        if (avatarLayout != nil)
        {
            self.avatarGroup.hidden = false;
            
            NSString *avatarUrl = avatarLayout[TGNeoMessageAvatarUrl];
            if (avatarUrl.length > 0)
            {
                [self.avatarGroup setBackgroundImageSignal:[TGBridgeMediaSignals avatarWithUrl:avatarUrl type:TGBridgeMediaAvatarTypeSmall] isVisible:self.isVisible];
            }
            else
            {
                NSString *initials = avatarLayout[TGNeoMessageAvatarInitials];
                UIColor *color = avatarLayout[TGNeoMessageAvatarColor];
                
                self.avatarLabel.hidden = false;
                self.avatarLabel.text = initials;
                [self.avatarGroup setBackgroundColor:color];
            }
        }
        
        NSValue *insetValue = metaGroupLayout[TGNeoContentInset];
        if (insetValue != nil)
        {
            UIEdgeInsets inset = insetValue.UIEdgeInsetsValue;
            [self.metaWrapperGroup setContentInset:inset];
        }
    }
}

- (IBAction)remotePressedAction
{
    if (self.buttonPressed != nil)
        self.buttonPressed();
}

- (void)setProcessingState:(bool)processing
{
    if (processing == _processing)
        return;
    
    _processing = processing;
    
    if (processing)
    {
        [self.audioIcon setImageNamed:_processingIconName];
        [self.audioIcon startAnimatingWithImagesInRange:NSMakeRange(0, 39) duration:0.65 repeatCount:0];
    }
    else
    {
        [self.audioIcon stopAnimating];
        [self.audioIcon setImageNamed:_normalIconName];
    }
}

+ (Class)rowControllerClassForMessage:(TGBridgeMessage *)message
{
    Class class = [TGNeoConversationRowController class];
    
    bool hasReplyHeader = false;
    bool hasForwardHeader = false;
    bool hasAttachments = false;
    
    for (TGBridgeMediaAttachment *attachment in message.media)
    {
        if ([attachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
        {
            hasAttachments = true;
            class = [TGNeoConversationMediaRowController class];
        }
        else if ([attachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
        {
            hasAttachments = true;
            class = [TGNeoConversationMediaRowController class];
        }
        else if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
        {
            hasAttachments = true;
            TGBridgeDocumentMediaAttachment *documentAttachment = (TGBridgeDocumentMediaAttachment *)attachment;
            if (documentAttachment.isSticker)
                class = [TGNeoConversationMediaRowController class];
        }
        else if ([attachment isKindOfClass:[TGBridgeLocationMediaAttachment class]])
        {
            hasAttachments = true;
            if (((TGBridgeLocationMediaAttachment *)attachment).venue == nil)
                class = [TGNeoConversationMediaRowController class];
        }
        else if ([attachment isKindOfClass:[TGBridgeAudioMediaAttachment class]])
        {
            hasAttachments = true;
        }
        else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
        {
            class = [TGNeoConversationStaticRowController class];
        }
        else if ([attachment isKindOfClass:[TGBridgeForwardedMessageMediaAttachment class]])
        {
            hasForwardHeader = true;
        }
        else if ([attachment isKindOfClass:[TGBridgeReplyMessageMediaAttachment class]])
        {
            hasReplyHeader = true;
        }
    }
    
    if (class == [TGNeoConversationRowController class] && !hasReplyHeader && !hasAttachments)
        class = [TGNeoConversationSimpleRowController class];
    
    return class;
}

@end
